MyBatis
1. 概述:
MyBatis是一个基于Java持久层的框架,内部封装了JDBC,简化了开发时对数据库驱动装载和连接的过程,使我们主要关注与sql语句的编写。MyBatis支持定制化SQL、存储过程以及高级映射,几乎避免了所有的JDBC代码和手动设置参数以及获取结果集。MyBatis支持使用XML和注解的方式来配置和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects 普通的Java对象)映射成数据库中的记录。
2. MyBatis的具体使用
- 在Maven中导入MyBatis的坐标
···
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
</dependencies>
···
-
MyBatis主配置文件
- 主配置文件的约束头信息
<?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">
- 主配置文件的配置内容和顺序
-property(属性) --property -settings(全局配置参数) --setting -typeAliases(类型别名) --typeAliases --package -typeHandlers(类型处理器) -objectFactory(对象工厂) -plugins(插件) -environments(环境集合属性对象) --ebvironment(环境子属性对象) ---transactionManager(事务管理) ---dataSource(数据源) -mappers(映射器) --mapper --package
- 简单的主配置文件
<?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> <!-- 配置Mybatis的环境 --> <environments default="mysql"> <!-- 配置mysql环境 --> <environment id="mysql"> <!-- 配置事务的类型 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据库的信息:使用数据库连接池(POOLED) --> <dataSource type="POOLED"> <!-- 配置数据库连接信息 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="123"/> </dataSource> </environment> </environments> <!-- 配置映射信息 使用XML配置时,使用resource属性 使用注解配置时,使用class属性或<package>标签体 只能使用一种方式配置 --> <mappers> <!-- 使用XML配置时 --> <!-- <mapper resource="com/hsh/study/dao/UserDao.xml"/> --> <!-- 使用注解配置时 --> <package name="com.hsh.study.dao"/> </mappers> </configuration>
- 常见的属性标签
- property(属性):可以使用两种方式来进行属性的配置
- 直接写在属性中
<properties> <property name="jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis" /> <property name="jdbc.username" value="root" /> <property name="jdbc.password" value="123" /> </properties>
- 导入外部配置文件中的属性
<!-- properties引入外部文件的两个属性 resource:要求配置文件必须位于类路径下 url:使用同一资源定位符 如:file:///D:/jdbcConfig.properties --> <properties resource="jdbcConfig.properties"> </properties>
- 数据源引用配置的属性
<dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource>
- typeAliases(类型别名):可以将全类名替换为别名
<typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="com.hsh.study.domain.User" /> <!-- 批量定义,扫描整个包下的类,别名为类名 --> <package name="com.hsh.study.domain.User" /> <package ... /> </typeAliases>
- mappers(映射器)
<!-- 第一种 相当于类路径的资源 --> <mappers resource="com/hsh/study/dao/UserDao.xml" /> <!-- 第二种 使用mapper接口类路径 要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中 --> <mappers class="com.hsh.study.dao.UserDao" /> <!-- 第三种 注册指定包下所有的mapper接口 要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中 --> <package name="com.hsh.study.dao" />
- property(属性):可以使用两种方式来进行属性的配置
-
MyBatis的执行方法
- 读取主配置文件:
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
- 创建SqlSessionFactoryBuilder对象:
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
- 使用builder对象构建SqlSessionFactory工厂对象:
SqlSessionFactory factory = builder.build(is);
- 使用工厂对象获取SqlSession对象:
SqlSession session = factory.openSession();
- 使用SqlSession对象构建出持久层接口的代理对象:
UserDao userDao = session.getMapper(UserDao.class);
- 使用代理对象执行数据库操作:
List<User> users = userDao.findAll();
- 提交事务(增删改):
session.commit();
- 查询操作执行遍历
- 关闭流
- 读取主配置文件:
2. MyBatis基于XML配置的使用
MyBatis在Maven中需要导入的坐标
MyBatis配置主配置文件及映射
-
MyBatis配置具体持久层接口的XML配置文件,需要创建出对应包目录结构的映射配置文件
- 持久层接口的约束头信息
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- 持久层接口的具体信息
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 配置映射信息 namespace:指定映射的类 --> <mapper namespace="com.hsh.study.dao.UserDao"> <!-- 配置查询所有的操作 id:对应的方法名称 resultType:指定结果集的类型 --> <select id="findAll" resultType="com.hsh.study.domain.User"> select * from user </select> </mapper>
-
基于XML映射的单表CRUD操作
- 查询所有
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 配置查询所有的操作 id:对应的方法名称 resultType:指定结果集的类型 --> <select id="findAll" resultType="com.hsh.study.domain.User"> select * from user </select> </mapper>
- 根据Id查询
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 根据Id查询 resultType:指定结果集的类型 parameterType:指定传入参数的类型 sql中使用的#{}字符:表示占位符,等同于JDBC中的?,具体内容由#{}中的内容决定的。 #{}中的内容:由于是基本数据类型,可以由自己随意指定 --> <select id="findById" resultType="com.hsh.study.domain.User" parameterType="int"> select * from user where id = #{uid} </select> </mapper>
- 添加
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 保存用户 parameterType:指定传入参数的类型,传入了一个对象 sql中使用的#{}字符:表示占位符,等同于JDBC中的?,具体内容由#{}中的内容决定的。 #{}中的内容:由于是参数类型是一个User对象,此时内容中应该写对象中的属性名。 此处使用了OGNL(Object Graphic Navigation Language) 对象图导航语言 语法格式:#{对象.对象} --> <insert id="saveUser" parameterType="com.hsh.study.domain.User"> <!-- 配置保存时获取插入的id --> <selectKey keyColumn="id" keyProperty="id" resultType="int"> select last_insert_id() </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> </mapper>
- 更新
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 更新用户 --> <update id="updateUser" parameterType="com.hsh.study.domain.User"> update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update> </mapper>
- 删除
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 删除用户 --> <delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete> </mapper>
- 模糊查询
- 使用占位符的sql语句
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 根据名称模糊查询 --> <select id="findByName" parameterType="String" resultType="com.hsh.study.domain.User"> select * from user where username like #{username} </select> </mapper>
写查询方法时:`List<User> users = userDao.findByName("%王%");` 执行中的代码如下:`select * from user where username like ?`
- 不适用占位符的sql语句
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 根据名称模糊查询 --> <select id="findByName" parameterType="String" resultType="com.hsh.study.domain.User"> select * from user where username like '%${value}%' </select> </mapper>
写查询方法时:`List<User> users = userDao.findByName("王");` 执行中的代码如下:`select * from user where username like '%王%'`
#{}和${}的区别: 1. #{} 表示一个占位符,自动进行java类型和jdbc类型转换,可以有效的防止sql注入。 2. ${} 表示拼接sql串,${}括号中只能是value。
- 动态SQL之<if>标签
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 使用别名定义后 --> <!-- 根据用户信息查询用户列表 --> <select id="findByUser" parameterType="user" resultType="user"> select * from user where 1=1 <if test="username != null and username != '' "> and username like #{username} </if> <if test="address != null and address != '' "> and address like #{address} </if> </select> </mapper>
- 动态SQL之<where>标签
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 首先可以定义一个默认的SQL语句 --> <sql id="defaultSql"> select * from user </sql> <!-- 根据用户信息查询用户列表 --> <select id="findByUser" parameterType="user" resultType="user"> <!-- 引入默认sql语句 --> <include refid="defaultSql"></include> <!-- 使用<where>标签代替where语句 --> <where> <if test="username != null and username != '' "> and username like #{username} </if> <if test="address != null and address != '' "> and address like #{address} </if> </where> </select> </mapper>
-
基于XML映射的多表CRUD操作
- 多对一查询(多个账户对应一个用户)
- 在账户实体类中引入用户对象
private User user;
,提供getter和setter方法 - 在对应的账户持久层接口配置文件中使用<resultMap>进行配置
<mapper namespace="com.hsh.study.dao.AccountDao"> <!-- 定义默认sql语句 --> <sql id="defaultSql"> select * from account </sql> <!-- 配置resultMap --> <resultMap id="accountMap" type="account"> <!-- 配置主键 --> <id property="id" column="aid" /> <!-- 配置其他属性 --> <result column="uid" property="uid" /> <result column="money" property="money" /> <!-- 指定引用实体类,使用association标签 --> <association property="user" javaType="user"> <!-- 配置主键 --> <id property="id" column="id" /> <!-- 配置其他属性 --> <result column="username" property="username" /> <result column="birthday" property="birthday" /> <result column="sex" property="sex" /> <result column="address" property="address" /> </association> </resultMap> <!-- 定义查询所有方法 --> <select id="findAll" resultMap="accountMap"> select u.*,a.id as aid,a.uid,a.money from account a,user u where u.id = a.uid; </select> </mapper>
- 在账户实体类中引入用户对象
- 一对多查询(一个用户对应多个账户)
- 在用户实体类中添加账户对象集合
private List<Account> accounts
,提供getter和setter方法 - 在对应的用户持久层接口配置文件中使用<resultMap>进行配置
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- 配置resultMap --> <resultMap id="userMap" type="user"> <id property="id" column="id" /> <result column="username" property="username" /> <result column="birthday" property="birthday" /> <result column="sex" property="sex" /> <result column="address" property="address" /> <!-- 配置账户的List集合,使用Collection标签 property:配置的集合 ofType:指定集合元素的数据类型 --> <collection property="accounts" ofType="account"> <!-- 配置主键 --> <id property="id" column="aid" /> <!-- 配置其他属性 --> <result column="uid" property="uid" /> <result column="money" property="money" /> </collection> </resultMap> <!-- 查询所有用户和用户下账户信息 --> <select id="findAllUserAccount" resultMap="userMap"> select u.*,a.id as aid,a.uid,a.money from user u left join account a on u.id = a.uid </select> </mapper>
- 在用户实体类中添加账户对象集合
- 多对一查询(多个账户对应一个用户)
-
基于XML映射的常用标签及属性
- resultMap标签:resultMap可以用于当实体类的类名和数据库的列名对应不上时建立对应关系,同时resultMap可以将查询结果映射为复杂类型。
- 属性:
- type:指定实体类的全限定类名
- id:给出一个唯一标识,是给查询select标签引用使用
- 子标签:
- id:用于指定主键字段
- result:用于指定非主键字段
- 属性:
- column:对应数据库中的列名
- property:实体类中的属性名
- 属性:
- 属性:
<mapper namespace="com.hsh.study.dao.UserDao"> <!-- resultMap的使用 --> <resultMap type="com.hsh.study.domain.User" id="userMap"> <id column="id" property="userId" /> <result column="username" property="userName" /> <result column="birthday" property="userBirthday" /> <result column="sex" property="userSex" /> <result column="address" property="userAddress" /> </resultMap> </mapper>
- 可以使用<assocation>和<collection>子标签针对实体类和集合进行封装。
- resultMap标签:resultMap可以用于当实体类的类名和数据库的列名对应不上时建立对应关系,同时resultMap可以将查询结果映射为复杂类型。
-
基于XML设置的延迟加载
- 延迟加载概念:只有当需要用到数据时才进行加载,不需要用到数据时不加载数据。
- 使用<assocation>标签实现延迟加载
- 指定从表的方向引用实体属性
<!-- 配置resultMap --> <resultMap id="accountMap" type="account"> <!-- 配置主键 --> <id property="id" column="aid" /> <!-- 配置其他属性 --> <result column="uid" property="uid" /> <result column="money" property="money" /> <!-- 指定从表的方向引用实体属性 select:需要调用的select映射的id column:需要传递给select映射的参数 --> <association property="user" javaType="user" select="com.hsh.study.dao.UserDao.findById" column="uid" > </association> </resultMap>
- 在主配置文件中添加延迟加载的配置
<setttings> <!-- 启用延迟加载 --> <setting name="lazyLoadingEnabled" value="true" /> <!-- --> <setting name="aggressiveLazyLoading" value="false" /> </settings>
- 使用<collection>标签实现延迟加载
- 指定从表的方向引用实体属性
<!-- 配置resultMap --> <resultMap id="userMap" type="user"> <id property="id" column="id" /> <result column="username" property="username" /> <result column="birthday" property="birthday" /> <result column="sex" property="sex" /> <result column="address" property="address" /> <!-- 配置账户的List集合,使用Collection标签 property:配置的集合 ofType:指定集合元素的数据类型 --> <collection property="accounts" ofType="account" select="com.hsh.study.dao.AccountDao.findByUid" column="id" > </collection> </resultMap>
- 在主配置文件中添加延迟加载的配置
<setttings> <!-- 启用延迟加载 --> <setting name="lazyLoadingEnabled" value="true" /> <!-- --> <setting name="aggressiveLazyLoading" value="false" /> </settings>
-
基于XML的缓存设置
- MyBatis中的缓存分为一级缓存和二级缓存
- 一级缓存:一级缓存是SqlSession级别的缓存,只要当SqlSession没有flush或close时,就存在。
- 二级缓存:二级缓存是mapper映射级别的缓存,多个SqlSession对象去操作同一个mapper映射的sql语句,多个SqlSession对象间可以共用二级缓存,二级缓存可以跨SqlSession。
- 开启二级缓存
- 主配置文件中开启二级缓存
<settings> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true" /> </settings>
- 配置相关的Mapper映射文件
<!-- 使用<cache>标签标识当前这个mapper映射将使用二级缓存,区分的标准就看mapper的namespace的值 --> <mapper namespace="com.hsh.study.dao.UserDao"> <!-- 开启二级缓存支持 --> <cache></cache> </mapper>
- 在需要应用的标签上使用useCache属性
<mapper namespace="com.hsh.study.dao.UserDao"> <select id="findById" resultType="user" parameterType="int" useCache="true"> select * from user where id = #{uid} </select> </mapper>
- MyBatis中的缓存分为一级缓存和二级缓存
3. MyBatis基于注解配置的使用
- MyBatis在Maven中导入坐标(同上)
- MyBatis配置主配置文件及映射
- MyBatis中的常用注解
- @Insert:实现插入操作
- @Update:实现更新操作
- @Delete:实现删除操作
- @Select:实现查询操作
- @Result:实现结果集的封装
- @Results:实现多个结果集的封装
- @One:实现一对一结果集封装
- @Many:实现一对多的结果集封装
- @SelectProvider:实现动态SQL映射
- @CacheNameSpace:实现注解二级缓存的使用
- 使用注解完成单表的CRUD操作
- 首先配置主配置文件,选择使用注解的方式
<?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> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="123"/> </dataSource> </environment> </environments> <mappers> <mapper class="com.hsh.study.dao.UserDao"/> </mappers> </configuration>
- 查询所有(其他略)
public interface UserDao { /** * 查询所有 * @return */ @Select("select * from user") List<User> findAll(); }
- 当实体类的属性和数据库列名不对应时
public interface UserDao { /** * 查询所有 * @return */ @Select("select * from user") @Results(id="userMap", value={ @Result(id=true,column="id",property="userId"), @Result(column="username",property="userName"), @Result(column="birthday",property="userBirthday"), @Result(column="sex",property="userSex"), @Result(column="address",property="userAddress") }) List<User> findAll(); }
- 引入ResultMap配置的查询操作
public interface UserDao { /** * 根据Id查询 * @return */ @Select("select * from user where id = #{uid}") @ResultMap("userMap") User findById(Integer userId); }
- 执行保存操作并返回主键值
public interface UserDao { /** * 保存用户 * @param user */ @Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})") @SelectKey(keyColumn = "id" , keyProperty = "id" , resultType = Integer.class , before = false , statement = { "select last_insert_id()" } ) void saveUser(User user); }
- 使用注解完成多表的CRUD操作及延迟加载
- 多对一(多个账户对应一个用户)
- @One的注解代替了<assocation>标签
- select:指定多表查询的sqlmapper
- fetchType:覆盖全局的配置参数lazyLoadingEnabled,应用延迟加载
- @One的注解代替了<assocation>标签
public interface AccountDao { /** * 查询所有并显示用户信息 * @return */ @Select("select * from account") @Results(id = "accountMap",value = { @Result(id = true, column = "id", property = "id"), @Result(column = "uid", property = "uid"), @Result(column = "money", property = "money"), @Result(column = "uid", property = "user", one = @One(select = "com.hsh.study.dao.UserDao.findById", fetchType = FetchType.LAZY)) }) List<Account> findAll(); }
- 一对多(一个用户对应多个账户)
- @Many的注解代替了<collection>标签
public interface UserDao { /** * 查询所有 * @return */ @Select("select * from user") @Results(id = "userMap", value = { @Result(id = true, column = "id", property = "id"), @Result(column = "uid", property = "uid"), @Result(column = "money", property = "money"), @Result(column = "id", property = "accounts", many = @Many(select = "com.hsh.study.dao.AccountDao.findByUid", fetchType = FetchType.LAZY ) ) }) List<User> findAll(); }
- 多对一(多个账户对应一个用户)
- 使用注解配置二级缓存
- 在主配置文件中开启二级缓存支持
<settings> <!-- 开启二级缓存支持 --> <setting name="cacheEnabled" value="true" /> </settings>
- 使用注解开启二级缓存
@CacheNamespace(blocking=true) public interface UserDao{ ··· }