mybatis 简记

概念

mybatis 一种“半自动化”的ORM(Object Relational Mapping,对象关系映射)实现。需要手动配置映射文件以实现数据库中的数据和java对象间的映射。

参考文章  mybatis 框架

 

mybatis 简记
mybatis 结构

 

mybatis 简记
mybatis 运行示意

 

 

*************************    简单项目    ***************************

 

mybatis 简记
项目结构

 

maven配置  -  pom.xml

<dependencies>

        <dependency>

            <groupId>org.mybatis</groupId>

            <artifactId>mybatis</artifactId>

            <version>3.4.6</version>

        </dependency>

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>8.0.17</version>  <!-- 注意版本 -->

        </dependency>

        <dependency>

            <groupId>log4j</groupId>

            <artifactId>log4j</artifactId>

            <version>1.2.17</version>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

            <version>3.8.1</version>

            <scope>test</scope>

        </dependency>

    </dependencies>

    <build>

        <resources>

            <resource>

                <directory>src/main/java</directory>

                <includes>

                    <include>**/*.xml</include>

                </includes>

            </resource>

            <resource>

                <directory>src/main/resources</directory>

                <includes>

                    <include>**/*.*</include>

                </includes>

            </resource>

        </resources>

    </build>

mybatis配置 - mybatis.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <properties resource="config.properties">  <!-- 引入外部资源配置文件,如果没有,resource项可以去掉 -->

        <property name="password" value="1234"/>  <!-- 自定义属性 -->

    </properties>

    <typeAliases>

        <!--<typeAlias type="com.shiyanlou.mybatis.entity.User" alias="User"/>-->  <!-- 单独定义某类的别名 -->

        <package name="com.shiyanlou.mybatis.entity"/> <!-- 包内所有类名作为该类的别名 -->

    </typeAliases>

    <!-- 配置mybatis运行环境 -->

    <environments default="development">

        <environment id="development">

            <!-- type="JDBC" 代表直接使用 JDBC 的提交和回滚设置 -->

            <transactionManager type="JDBC" />

            <!-- POOLED 表示支持JDBC数据源连接池 -->

            <!-- 数据库连接池,由 Mybatis 管理,数据库名是 mybatis,MySQL 用户名 root,密码为空 -->

            <dataSource type="POOLED">

                <property name="driver" value="${driver}" />

                <property name="url" value="${url}" />   <!-- 引用外部配置中的变量 -->

                <property name="username" value="root" />

                <property name="password" value="${password}" /> <!-- 引用上述自定义属性中的变量 -->

            </dataSource>

        </environment>

    </environments>

    <mappers>

        <!--<mapper class="com.shiyanlou.mybatis.mapper.UserMapper"/>--> <!-- 单独指定定义某个映射类 -->

        <package name="com.shiyanlou.mybatis.mapper"/>  <!-- 定义该包类所有的类均为映射类 -->

    </mappers>

</configuration>

 

外部配置文件 - config.properties

driver=com.mysql.cj.jdbc.Driver

url=jdbc:mysql://localhost:3306/mybatis_test?serverTimezone=GMT%2B8

username=root

#password=1234

 

日志配置  - log4j.properties

# Global logging configuration

log4j.rootLogger=DEBUG, stdout

# Console output...

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

 

实体类 - User.java

public class User {

    private Integer id;

    private String username;

    private String password;

    private String sex;

    private Stringaddress;

    // 此处省略 getter 和 setter

}

 

映射类 - UserMapper.java

public interface UserMapper {

    /*

    * 新增用戶

    * @param user

    * @return

    */

    int insertUser(User user);

    /*

    * 修改用戶

    * @param user

    * @param id

    * @return

    */

    int updateUser(User user);

    /*

    * 刪除用戶

    * @param id

    * @return

    */

    int deleteUser(Integer id);

    /*

    * 根据id查询用户信息

    * @param id

    * @return

    */

    User selectUserById(Integer id);

    /*

    * 查询所有的用户信息

    * 不建议使用,没有分页,数据量过大时,造成内存浪费,基本没有使用场景  

    * @return

    */

    List<User> selectAllUser();

}

 

映射xml文件 - UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.shiyanlou.mybatis.mapper.UserMapper">

    <!-- 自定义返回结果集; property - java属性;  column -  数据表列名; javaType - java类型 -->

    <resultMap id="userMap" type="User">

        <id property="id" column="id" javaType="int"></id>  <!-- 主键映射 -->

        <result property="username" column="username" javaType="String"></result>  <!-- 普通属性映射 -->

        <result property="password" column="password" javaType="String"></result>

        <result property="sex" column="sex" javaType="String"></result>

        <result property="address" column="address" javaType="String"></result>

    </resultMap>

    <!--<cache eviction="LRU" flushInterval="30000" size="1000"/>-->  <!-- 缓存配置,这里不细述 -->

    <!-- 定义 SQL 语句,其中 id 需要和接口中的方法名一致 -->

    <!-- useGeneratedKeys:实现自动生成主键 -->

    <!-- keyProperty: 唯一标记一个属性 -->

    <!-- parameterType 指明查询时使用的参数类型,resultType 指明查询返回的结果集类型 -->

    <insert id="insertUser" useGeneratedKeys="true" keyProperty="id" parameterType="User">

        insert into user (username,password,sex,address) values

        (#{username},#{password},#{sex},#{address})

    </insert>

    <update id="updateUser"  parameterType="User">

        update user set

        address=#{address} where

        id=#{id}

    </update>

    <delete id="deleteUser" parameterType="int">

        delete from user where

        id=#{id}

    </delete>

    <!-- 如未为 Java Bean 起类别名,使用 resultType="com.shiyanlou.mybatis.model.User" 替代  resultMap -->

    <!-- 使用resultType时,一定要保证,你属性名与字段名相同;如果不相同,就使用自定义的resultMap -->

    <select id="selectUserById" parameterType="int" resultMap="userMap" >

        select * from user where id=#{id}

    </select>

    <select id="selectAllUser" resultMap="userMap"> <!-- 不需要设置为collection,底层会自动将数据转换为列表-->

        select * from user

    </select>

</mapper>

 

测试类 - UserTest.java   (位于测试文件夹下)  注意,使用 TestCase 作为测试代理,方法签名一定要以 test 开头,访问权限为 public .

 

mybatis 简记
测试文件位置

 

import junit.framework.TestCase;

 

public class UserTest extends TestCase {

 

    private static SqlSessionFactory sqlSessionFactory;

 

    static {

        // Mybatis 配置文件

        String resource = "mybatis.cfg.xml";

        // 得到配置文件流

        InputStream inputStream = null;

        try {

            inputStream = Resources.getResourceAsStream(resource);

        } catch (IOException e) {

            e.printStackTrace();

        }

        // 创建会话工厂,传入 MyBatis 的配置文件信息

        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    }

 

    // 新增用戶

    public void testInsertUser() {

        // 通过工厂得到 SqlSession

        SqlSession session = sqlSessionFactory.openSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        User user = new User();

        user.setUsername("Liry");

        user.setPassword("123abc");

        user.setSex("female");

        user.setAddress("Hongkong");

        try {

            mapper.insertUser(user);

            session.commit();

        } catch (Exception e) {

            e.printStackTrace();

            session.rollback();

        }

        // 释放资源

        session.close();

    }

 

    // 更新用戶

    public void testUpdateUser() {

        SqlSession session = sqlSessionFactory.openSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        User user = null;

        try {

            user = mapper.selectUserById(5);

        } catch (Exception e1) {

            e1.printStackTrace();

        }

        user.setAddress("chongqing");

        try {

            mapper.updateUser(user);

            session.commit();

        } catch (Exception e) {

            e.printStackTrace();

            session.rollback();

        }

        session.close();

    }

 

    // 删除用戶

    public void testDeleteUser() {

        SqlSession session = sqlSessionFactory.openSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        try {

            mapper.deleteUser(3);

            session.commit();

        } catch (Exception e) {

            e.printStackTrace();

            session.rollback();

        }

        session.close();

    }

 

    // 根据id查询用户信息

    public void testSelectUserById() {

        SqlSession session = sqlSessionFactory.openSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        try {

            User user = mapper.selectUserById(null);

            session.commit();

            System.out.println(user.getId() + " " + user.getUsername() + " "

                    + user.getPassword() + " " + user.getSex() + " "

                    + user.getAddress());

        } catch (Exception e) {

            e.printStackTrace();

            session.rollback();

        }

        session.close();

    }

 

    // 查询所有的用户信息

    public void testSelectAllUser() {

        SqlSession session = sqlSessionFactory.openSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        try {

            List<User> userList = mapper.selectAllUser();

            session.commit();

            for (User user : userList) {

                System.out.println(user.getId() + " " + user.getUsername() + " "

                        + user.getPassword() + " " + user.getSex() + " "

                        + user.getAddress());

            }

        } catch (Exception e) {

            e.printStackTrace();

            session.rollback();

        }

        session.close();

    }

 

*************************    要点    ***************************

 

● 多数据源

生成session工厂实例

InputStream inputStream = Resources.getResourceAsStream("mybatis.cfg.xml");

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

其中 build 方法,允许传入环境配置

build(InputStream inputStream)

build(InputStream inputStream, String environment)

一个 SqlSessionFactory  只对应一个数据库,则多数据源需要生成多个 SqlSessionFactory  实例。

// 配置1

InputStream inputStream1 = Resources.getResourceAsStream("mybatis1.cfg.xml");

SqlSessionFactory sqlSessionFactory1 = new SqlSessionFactoryBuilder().build(inputStream1);

// 配置2

InputStream inputStream2 = Resources.getResourceAsStream("mybatis2.cfg.xml");

SqlSessionFactory sqlSessionFactory2 = new SqlSessionFactoryBuilder().build(inputStream2);

 

● 有意思的自定义结果集

如果使用构造方法,可以这样自定义结果集:

<resultMap id="userMap" type="User">

    <constructor> 

        <idArg column="id" javaType="int"/>  <!-- 指出主键参数 -->

        <arg column="username" javaType="String"/>  <!-- 其他普通参数 -->

        <arg column="password" javaType="String"/>

        <arg column="sex" javaType="String"/>

        <arg column="address" javaType="String"/>

    </constructor> 

</resultMap>

discriminator 鉴别器,选择性地返回字段 (注意,只是针对已经查询到的结果选择性返回部分字段,不会影响结果数)

<resultMap id="userMap" type="User">

    <id property="id" column="id" javaType="int"/>

    <result property="username" column="username" javaType="String"/>

    <result property="password" column="password" javaType="String"/>

    <discriminator javaType="String" column="sex">   <!-- 相当于java中的 switch -->

        <case value="male" resultType="sexResult">

            <!-- 符合条件则该字段被返回,否则返回空,若存在其他case,则也可能返回其他结果 -->

            <result property="sex" column="sex" javaType="String"/> 

        </case>

    </discriminator>

    <result property="address" column="address" javaType="String"/>

</resultMap>

 

● 不支持自增长的数据插入

先查询主键,再插入

<insert id="insertUser">

    <selectKey keyProperty="id" resultType="int" order="BEFORE">

        select seq_users.nextval from dual

    </selectKey>

    insert into user (username,password,sex,address) values (#{username},#{password},#{sex},#{address})

</insert>

 

● 开启缓存

启用此功能可以缓存一定范围内的查询结果,单体应用可以作为备选配置,详情参考文章开头给出的文章地址。

● 一对多、多对一、多对多关系映射

在标签 resultMap 中使用 association 表示单个实体,collection 表示集合

<resultMap id="classmap" type="Classes">

        <id property="id" column="c_id"/>

        <result property="name" column="c_name"/>

        <!-- 若不使用 resultMap,则写法同 collection,无需指定 column (对应实体表主键列) -->

        <association property="teacher" column="c_ht_id" resultMap="teacherMap"/> 

        <!--<collection property="students" resultMap="studentsMap"/>-->

        <collection property="students" ofType="Student">  <!-- ofType 为集合中的元素类型 -->

            <id property="id" column="s_id"/>

            <result property="name" column="s_name"/>

            <result property="sex" column="s_sex"/>

            <result property="age" column="s_age"/>

        </collection>

</resultMap>

● 动态SQL

一些常用标签:

- if  动态条件

- choose  同一字段多种条件

- trim  动态增加或消除前缀或后缀

- where  智能处理and或or的增加或消除

- set  智能处理update多个字段的分隔符

- foreach  循环迭代

- bind  产生下文可引用的变量并赋值

● 注解

在java的mapper文件中使用注解,替代在xml文件写sql。(不提倡)

*************************    一些底层    ***************************

● mybatis 如何防止注入攻击?

因注入攻击只能发生在SQL编译过程,因此:

- #  使用这种符号的sql语句将会执行预编译,执行时将会使用已编译好的SQL语句,替换“?”占位符。

- $  使用这种符号时,${}中的内容将会直接出现在编译过程中,因此不能防止注入攻击。典型的使用情形是动态替换表名或列名。