MyBatis学习简单总结

前言

利用端午时间简单学习了MyBatis,现根据学习内容进行简单总结。

知识点

1. MyBatis框架简介

MyBatis是一个持久化框架,使用Java编写,封装了JDBC的很多实现细节,可以使开发者只关注SQL语句,而无需关注注册驱动/建立连接等繁杂的过程,封装过程使用ORM思想来实现。

ORM:Object Relational Mapping 对象关系映射,把数据库表和实体类的属性对应起来,使得可以通过操作实体类来实现数据库表

2. 基于XML和基于注解的开发实例

以User为实体类,构建简单的一个入门实例,通过MyBatis操作实体类完成对数据库user表的CRUD操作,之后分别会用基于XML和基于注解的方式完成。

2.1 建表

通过Navicat连接MySQL完成建表操作。

2.1.1 启动mysql服务【管理员模式打开cmd】

2.1.2 创建User表并插入数据

2.2 导入依赖

创建maven项目,导入mybatis/mysql-connection等依赖

    <dependencies>
        <!-- MyBatis依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- mysql依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <!-- log4j依赖 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.12.1</version>
        </dependency>

        <!-- 单元测试依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.3 创建User实体类/IUserDao接口

2.3.1 根据数据库字段创建User实体类并定义属性和getter/setter方法

/**
 * User实体类
 */
public class User implements Serializable {
    private Long userId;
    private String username;
    private String password;
    private Date birthday;
    private String sex;
    private String address;

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

2.3.1 创建User接口类,并根据查询所有用户功能创建抽象方法

public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    List<User> findAllUsers(); //public abstract已省略
}

2.4 添加映射配置文件

配置文件主要是配置数据库连接信息以及为方便给类名起别名,配置映射文件的路径

<?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">
<!--MyBatis的主配置文件-->
<configuration>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql环境-->
        <environment id="mysql">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源/连接池-->
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>
        <mapper resource="com/practice/dao/IUserDao.xml"/>
    </mappers>
</configuration>

2.5 基于XML模式的映射文件及查询所有用户功能测试

基于XML模式主要是映射配置文件中mapper中会指定resource为映射文件的全限定路径,另外根据IUserDao接口的方法编写映射文件。

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace锁定接口 -->
<mapper namespace="com.practice.dao.IUserDao"> 
    <!-- id锁定方法,resultType为方法返回类型,另外还有parameterType表示方法参数类型 -->
    <select id="findAllUsers" resultType="com.practice.domain.User">
        select * from user
    </select>
</mapper>
public class IUserTest {

    private InputStream in;
    private SqlSession session;
    private IUserDao userDao;

    /**
     * 初始化操作
     */
    @Before   //每一个测试之前都需要初始化
    public void init() throws IOException {
        //1.读取映射配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.构建SqlSessionFactory工厂对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.构建SqlSession会话对象
        session = factory.openSession();
        //4.构建代理IUserDao对象
        userDao = session.getMapper(IUserDao.class);
    }

    /**
     * 测试查询所有用户
     */
    @Test
    public void testFindAllUsers(){
        List<User> users = userDao.findAllUsers();
        for(User user : users){
            System.out.println(user);
        }
    }

    @After //在每一个测试之后都需要释放资源
    public void destroy() throws IOException {
        //释放资源
        session.close();
        in.close();
    }
}

测试结果即查询到所有的用户信息。

2.6 基于注解的接口及查询所有用户功能测试

基于注解主要是不使用映射文件,映射配置文件中的mapper使用class属性,值为接口的全限定路径。

public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user") //通过此注解完成对数据库的操作。package+方法名对比xml中的namespace,接口方法对比xml中的id,注解中的sql对比xml中的sql,方法的参数和返回值对比xml中的parameterType和resultType
    List<User> findAllUsers();
}

基于注解的方法测试同基于xml的方法测试,测试结果相同。后续其他CRUD功能都会分别以基于XML和基于注解进行说明。

2.7 一般开发中使用上述中的代理模式进行开发,但是也可以通过daoImpl实现,此处以基于xml方式+daoimpl进行简单说明一下

创建实现IUserDao的实现类IUserDaoImpl,并在方法中创建SqlSession。调用SqlSession的selectList方法,参数为namespace+id。SqlSession还支持insert/selectOne/delete/update方法, 参数也可以加Object对象(例如insert的参数为路径和user对象)

public class IUserDaoImpl implements IUserDao {
    private SqlSessionFactory sessionFactory;

    /**
     * 通过构造函数保证SqlSessionFactory不为空
     * @param sqlSessionFactory
     */
    public IUserDaoImpl(SqlSessionFactory sqlSessionFactory){
        this.sessionFactory = sqlSessionFactory;
    }

    public List<User> findAllUsers() {
        //1.使用工厂创建SqlSession对象
        SqlSession session = sessionFactory.openSession();
        //2.使用session执行查询所有方法
        List<User> users = session.selectList("com.practice.dao.IUserDao.findAllUsers");
        //释放资源
        session.close();
        //3.返回查询结果
        return users;
    }
}

由于这种开发比较繁琐,此处只是简单说明。目前用到的基本都是基于xml的开发模式。

2.8 typeAliases与package的使用

在使用中可能会有多个实体类,为了xml中参数方便不再重复写全限定路径,可以中typeAiases标签在映射配置文件中进行设置。

如果实体类比较多的话,可以使用package标签。

另外,映射配置文件中mapper标签也支持package

3. MyBatis的基本操作

主要是记录xml和注解的表达,测试和上述测试类似,差别的地方在于调用接口的不同方法,不再赘述。xml和注解两种开发不可并存,否则运行会报错(即不能有注解之后还有xml映射文件)

3.1 根据ID查询用户

3.1.1 基于xml

<!-- 根据用户ID查询用户 -->
<select id="findById" parameterType="LONG" resultType="com.practice.domain.User">
    select * from user
    where userId=#{userId}
</select>

3.1.2 基于注解

@Select("select * from user where userId=#{userId}")
@ResultMap(value = {"userMap"})
User findByUserId(Long userId);

3.2 根据用户名模糊查询用户

3.2.1 基于XML

<!-- 根据用户名模糊查询用户 -->
    <!-- preparedStatement对象 -->
    <select id="findByName" parameterType="java.lang.String" resultType="com.practice.domain.User">
        select * from user
        where username like #{username}
    </select>
    <!-- statement对象 -->
<!--    <select id="findByName" parameterType="java.lang.String" resultType="com.practice.domain.User">-->
<!--        select * from user-->
<!--        where username like '%${value}%'-->
<!--    </select>-->

3.2.2 基于注解

@Select("select * from user where username like #{username}") //preparedStatement,较为常用
//    @Select("select * from user where username like '%${value}%' ") //statement
    @ResultMap("userMap")
    List<User> findByUsername(String username);

3.3 增加用户

3.3.1 基于XML

<!-- 添加一个用户 -->
    <insert id="saveUser" parameterType="USER">
    <!-- 配置插入操作后,返回插入数据的ID -->
    <selectKey keyProperty="userId" keyColumn="userId" resultType="LONG" order="AFTER">
        select last_insert_id();
    </selectKey>
        insert into user(username,password,birthday,sex,address)
        values(#{username},#{password},#{birthday},#{sex},#{address})
    </insert>

3.3.2 基于注解

@Insert("insert into user(username,password,birthday,sex,address) " +
        "values(#{username},#{password},#{birthday},#{sex},#{address})")
void saveUser(User user);

3.4 更新用户

3.4.1 基于XML

<!-- 更新一个用户 -->
<update id="updateUser" parameterType="uSer">
    update user set
    username=#{username},password=#{password},birthday=#{birthday},sex=#{sex},address=#{address}
    where userId=#{userId}
</update>

3.4.2 基于注解

@Update("update user set username=#{username},password=#{password}," +
        "birthday=#{birthday},sex=#{sex},address=#{address} where userId=#{userId}")
void updateUser(User user);

3.5 删除用户

3.5.1 基于XML

<!-- 删除一个用户 #{占位符是可以随意定义的}-->
<delete id="deleteUser" parameterType="java.lang.Long">
    delete from user
    where userId=#{userId}
</delete>

3.5.2 基于注解

@Delete("delete from user where userId=#{userId}")
void deleteUser(Long userId);

3.6 resultMap的使用

一般开发中会遇到实体类的属性名和数据库字段名不一致的情况,可以通过ResultMap进行映射匹配,例如数据库中user_id对应实体类中的userId,分别基于xml和基于注解进行属性映射

3.6.1 基于XML

 <!-- 配置 查询结果的列名和实体类属性名的对应关系 -->
    <resultMap id="userMap" type="com.practice.domain.User">
        <!-- 主键字段的对应 -->
        <id property="user_id" column="user_id"></id>
        <!-- 非主键字段的对应 -->
        <result property="userName" column="username"></result>
        <result property="password" column="password"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
    </resultMap>

<!-- 使用提前配置数据库字段与实体类属性名的对应关系后,此处resultType需要改成userMap(即导入对应关系配置)
        parameterMap同理,如果参数为实体类的话需要使用parameterMap="userMap"-->
    <select id="findAllUsers" resultMap="userMap">
        select * from user
    </select>

3.6.2 基于注解

@Select("select * from user")
    @Results(id="userMap", //声明id之后其他使用该映射规则的直接使用resultMap,然后写入这个id即可
            value = {@Result(id = true,column = "user_id",property = "userId"),
                    @Result(column = "username",property = "username"), //非主键属性,id默认为false
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "sex",property = "sex"),
                    @Result(column = "address",property = "address")}
    )
    List<User> findAllUsers();

4. MyBatis的多表查询操作

4.1 多对一:例如多个账户可以隶属于同一个用户

在MyBatis中多对一可以看作一对一,对于一个账户而言,只有一个用户。实现功能:查询所有的账户并返回账户对应的用户信息,通过关联属性

4.1.1 在账户Account实体类中建立关联属性User

4.1.2 基于xml实现关联

<resultMap id="accountUserMap" type="com.practice.domain.Account">
        <id property="accountId" column="accountId"></id>
        <result property="userId" column="userId"></result>
        <result property="money" column="money"/>
        <!-- 一对一的关系映射,配置封装user的内容 -->
        <association property="user" column="userId" javaType="com.practice.domain.User">
            <id property="userId" column="userId"/>
            <result property="username" column="username"/>
            <result property="password" column="password"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </association>
    </resultMap>

4.1 基于注解实现关联

@Select("select * from account")
    @Results(id = "accountMap", value = {
            @Result(id=true,column = "accountId",property = "accountId"),
            @Result(column = "userId",property = "userId"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "userId",
                    one = @One(select="com.practice.dao.IUserDao.findByUserId"))
    })
    List<Account> findAllAccounts();

4.2 一对多:例如一个用户可以拥有多个账户

实现功能:查询所有用户并返回用户对应的所有账户信息,通过集合属性

4.2.1 User实体类中创建集合属性Account

4.2.2 基于xml实现集合

<!-- 定义User的resultMap -->
    <resultMap id="userMap" type="com.practice.domain.User">
        <id property="userId" column="userId"></id>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="roles" ofType="role">
            <id property="roleId" column="roleId"></id>
            <result property="rolename" column="rolename"></result>
        </collection>
    </resultMap>

4.2.3 基于注解实现集合

@Select("select * from user")
    @Results(id="userMap",
            value = {@Result(id = true,column = "userId",property = "userId"),
                    @Result(column = "username",property = "username"),
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "sex",property = "sex"),
                    @Result(column = "address",property = "address"),
            @Result(property = "accounts",column = "userId",
                    many = @Many(select="com.practice.dao.IAccountDao.findByUserId"))} 
    )
    List<User> findAllUsers();

查询的时候直接调用User对象的getAccounts方法即可。

4.3 多对多:可以划分为两个一对多关系,分别使用集合属性完成,此处不再赘述

5. MyBatis中的连接池

MyBatis支持三种数据库连接方式,POOLED/UNPOOLED/JNDI

5.1 POOLED

采用传统的java.sql.DataSource规范中的连接池,MyBatis中有针对规范的实现

5.2 UNPOOLED

采用传统的获取连接的方式,虽然也实现java.sql.DataSource接口,但是并没有使用池的思想,只是单纯建立数据库连接

5.3 JNDI

采用服务器提供的JNDI技术来获取DataSource对象,不同的服务器所能拿到的DataSource不同。如果不是web/maven的war工程是不能使用的,tomcat服务器中采用连接池为dbcp连接池

6. MyBatis中的加载

6.1 立即加载

不管用不用,只要一调用方法,马上发起查询。一般一对一使用立即加载。

6.1.1 基于XML的立即加载

<resultMap id="accountUserMap" type="com.practice.domain.Account">
        <id property="accountId" column="accountId"></id>
        <result property="userId" column="userId"></result>
        <result property="money" column="money"/>
        <!-- select属性指定的内容:查询用户的唯一标识
             column属性指定的内容:用户根据id查询时所需要的参数值-->
        <association property="user" column="userId" javaType="user"
                     select="com.practice.dao.IUserDao.findById"></association>
    </resultMap>
    <select id="findAllAccounts" resultMap="accountUserMap">
        select * from account
    </select>

6.1.2 基于注解的立即加载,FetchType为EAGER

@Select("select * from account")
    @Results(id = "accountMap", value = {
            @Result(id=true,column = "accountId",property = "accountId"),
            @Result(column = "userId",property = "userId"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "userId",
                    one = @One(select="com.practice.dao.IUserDao.findByUserId",fetchType = FetchType.EAGER))
    })
    List<Account> findAllAccounts();

6.2 延迟加载

在真正使用数据时才发起查询,不用的时候不查询,按需加载(懒加载)。一般一对多使用延迟加载

无论是xml还是注解,都需要在映射配置文件中设置开启延迟加载。

6.2.1 基于XML的延迟加载

<!-- 定义User的resultMap -->
    <resultMap id="userMap" type="com.practice.domain.User">
        <id property="userId" column="userId"></id>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="account" ofType="account"
                    select="com.practice.dao.IAccountDao.findAccountByUserId" column="userId"></collection>
    </resultMap>

    <select id="findAllUsers" resultMap="userMap">
        select * from user
    </select>
<!-- 查询用户直接通过这个select查,如果需要查询账户,则使用userId作为参数去IAccountDao的映射文件中去找findAccountByUserId去查 -->

6.2.2 基于注解的延迟加载,FetchType为LAZY

@Select("select * from user")
    @Results(id="userMap",
            value = {@Result(id = true,column = "userId",property = "userId"),
                    @Result(column = "username",property = "username"),
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "sex",property = "sex"),
                    @Result(column = "address",property = "address"),
            @Result(property = "accounts",column = "userId",
                    many = @Many(select="com.practice.dao.IAccountDao.findByUserId",fetchType = FetchType.LAZY))}
    )
    List<User> findAllUsers();

7. MyBatis中的缓存

7.1 一级缓存

MyBatis中SqlSession对象的缓存。当执行查询之后,查询的结果会同时存入到SqlSession提供的一块区域中,区域的结构是一个Map。当再次查询相同数据时MyBatis会先去SqlSession中查询是否有,有的话会直接拿来用。SqlSession消失后这个缓存Map也会随之消失

7.2 二级缓存

MyBatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

7.2.1 基于XML开发中二级缓存的开启方式

1. 在映射配置文件配置让MyBatis支持二级缓存

2. 在映射文件中配置

3. 在具体的方法中配置

7.2.2 基于注解开发中二级缓存的开启方式

1. 在映射配置文件中配置

2. 在接口类中使用注解进行配置

总结

千里之行始于足下,积少成多,认真踏实,好好学习,努力进步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章