MyBatis
初识Mybatis
1.导入mybatis相关依赖
<dependencies>
<!--mybatis的核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
2.创建pojo,将pojo和数据表进行关联映射,定义接口
创建Bean :User类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
定义数据持久层接口类:UserDao
public interface UserDao {
List<User> queryAll();
}
3.创建mybatis核心配置文件sqlMapConfig.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>
<!--配置mybatis的环境-->
<environments default="mysql">
<!--配置mysql的环境-->
<environment id="mysql">
<!--配置事务的类型,我们使用JDBC的事务-->
<transactionManager type="JDBC"></transactionManager>
<!--
配置连接数据库的信息,用的是数据源(连接池)
type: POOLED 使用默认的数据库连接
UNPOOLED
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="rootroot"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/duoduo/dao/UserDao.xml"></mapper>
</mappers>
</configuration>
4.创建接口的映射文件
接口所在的目录和接口文件的目录保持一致。
<?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">
<mapper namespace="com.duoduo.dao.UserDao">
<!--
配置查询所有的操作
id:方法名称 和接口的方法名称对应
parameterType: 参数值类型(全限定名)
resultType: 返回值类型(全限定名)
-->
<select id="queryAll" resultType="com.duoduo.pojo.User">
select * from user
</selct>
</mapper>
5.编写测试代码
@Test
public void testQueryAll() throws Exception {
//加载mybatis的核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//创建sqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//创建SqlSessionFactory对象(构建者设计模式,屏蔽了SqlSessionFactory创建的细节)
SqlSessionFactory factory = builder.build(inputStream);
//创建SqlSession对象(工厂模式)
SqlSession session = factory.openSession();
//通过SqlSession生成接口的代理对象(生成动态代理的思想)
UserDao userDao = session.getMapper(UserDao.class);
List<User> users = userDao.queryAll();
users.forEach(user -> System.out.println(user));
//关闭资源
session.close();
inputStream.close();
}
测试结果:
单表操作(UserDao.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">
<mapper namespace="com.duoduo.dao.UserDao">
<!--
查询所有的用户信息
-->
<select id="queryAllUser" resultType="com.duoduo.pojo.User">
select * from user
</select>
<!--
根据id查询用户信息
id:定义接口的名称
resultType: 方法的返回值类型
parameterType: 形参的数据类型
-->
<select id="queryUserById" resultType="com.duoduo.pojo.User" parameterType="int">
select * from user where id = #{id}
</select>
<insert id="addUser" parameterType="com.duoduo.pojo.User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
<!--
selectKey 获取自增长主键的标签
keyColum:数据表中主键的字段名称
keyProperty:实体类映射主键字段的属性
resultType:主键的数据类型
select 240308 是mysql的系统函数,专门用来获取新增之后主键的值
-->
<!--<selectKey keyColumn="id" keyProperty="id" resultType="Integer">
select 240308
</selectKey>-->
insert into user(username, birthday, sex, address) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
<update id="updateUser" parameterType="com.duoduo.pojo.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id = #{id}
</update>
<delete id="deleteUserById" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
模糊查询
UserDao.xml 配置文件的编写
<select id="queryDimUserByUsername" parameterType="string" resultType="com.duoduo.pojo.User">
select * from user where username like #{username}
</select>
<select id="queryDimUserByUsername2" parameterType="string" resultType="com.duoduo.pojo.User">
select * from user where username like '%${value}%'
</select>
测试类的编写,不同的写法测试类也不同
@Test
public void testDimQuery(){
List<User> users = userDao.queryDimUserByUsername("%王%");
users.forEach(user -> System.out.println(user));
session.commit();
}
@Test
public void testDimQuery2(){
List<User> users = userDao.queryDimUserByUsername2("王");
users.forEach(user -> System.out.println(user));
session.commit();
}
#{} 和 ${} 的区别:
#{}表示一个占位符号 通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换, #{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类 型值,#{}括号中可以是 value 或其它名称。
{}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, {}括号中只能是value。**(使用的是statement)
在接口中传入多个参数的问题
数据控制层Dao
List<User> queryUserByMap(Map<String,Object> map);
UserDao.xml 配置文件的编写
<select id="queryUserByMap" parameterType="map" resultType="com.duoduo.pojo.User">
select * from user where sex = #{sex} and address = #{address}
</select>
测试类:
public void testQueryMap(){
Map<String,Object> map = new HashMap<>();
map.put("sex","男");
map.put("address","广东深圳");
List<User> users = userDao.queryUserByMap(map);
users.forEach(user -> System.out.println(user));
}
pojo封装同一类型参数的对象
@Getter
@Setter
public class QueryIds {
private List<Integer> ids;
}
数据控制层Dao
List<User> queryUserByIds(QueryIds queryIds);
UserDao.xml 配置文件的编写
<select id="queryUserByIds" parameterType="com.duoduo.pojo.QueryIds" resultType="com.duoduo.pojo.User">
select * from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
测试类:
public void testQueryIds(){
List<Integer> ids = new ArrayList<>();
Collections.addAll(ids,41,43,45,52);
QueryIds queryIds = new QueryIds();
queryIds.setIds(ids);
List<User> users = userDao.queryUserByIds(queryIds);
users.forEach(user -> System.out.println(user));
}
用集合实现
数据控制层Dao
List<User> queryUserByList(List<Integer> ids);
UserDao.xml 配置文件的编写
<select id="queryUserByList" parameterType="list" resultType="user">
select * from user where id in
<foreach collection="collection" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
测试类:
public void testQueryByList(){
List<Integer> ids = new ArrayList<>();
Collections.addAll(ids,41,43,45,52);
List<User> users = userDao.queryUserByList(ids);
users.forEach(user -> System.out.println(user));
}
用集合实现2
数据控制层Dao
在形参前面添加一个@Param() 注解 ,里面的内容是后续UserDao.xml里面需要传入的collection值
List<User> queryUserByList(@Param("ids") List<Integer> ids);
UserDao.xml 配置文件的编写
foreach标签里面 collection的值引用的是Dao层@Param注解里面的值
<select id="queryUserByList" parameterType="list" resultType="user">
select * from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
测试类:
public void testQueryByList(){
List<Integer> ids = new ArrayList<>();
Collections.addAll(ids,41,43,45,52);
List<User> users = userDao.queryUserByList(ids);
users.forEach(user -> System.out.println(user));
}
用数组实现
数据控制层Dao
List<User> queryUserByArray(Integer[] arr);
UserDao.xml 配置文件的编写
<select id="queryUserByArray" parameterType="int[]" resultType="user">
select * from user where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
测试类:
public void testQueryByArray(){
Integer[] arr = {41,43,45,52};
List<User> users = userDao.queryUserByArray(arr);
users.forEach(user -> System.out.println(user));
}
用数组实现2
数据控制层Dao
在形参前面添加一个@Param() 注解 ,里面的内容是后续UserDao.xml里面需要传入的collection值
List<User> queryUserByArray(@Param("ids") Integer[] arr);
UserDao.xml 配置文件的编写
foreach标签里面 collection的值引用的是Dao层@Param注解里面的值
<select id="queryUserByArray" parameterType="int[]" resultType="user">
select * from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
测试类:
public void testQueryByArray(){
Integer[] arr = {41,43,45,52};
List<User> users = userDao.queryUserByArray(arr);
users.forEach(user -> System.out.println(user));
}
SqlMapConfig.xml的一些细节
SqlMapConfig.xml 中配置的内容和顺序
-properties(属性)
--property
-settings(全局配置参数)
--setting
-typeAliases(类型别名)
--typeAliase
--package
-typeHandlers(类型处理器)
-objectFactory(对象工厂)
-plugins(插件)
-environments(环境集合属性对象)
--environment(环境子属性对象)
---transactionManager(事务管理)
---dataSource(数据源)
-mappers(映射器)
--mapper
--package
properties标签
读取properties文件内的信息
<properties resource="db.properties"></properties>
<environments default="mybatis">
<environment id="mybatis">
<transactionManager type="JDBC"></transactionManager>
<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>
</environment>
</environments>
typeAliases标签
自定义别名方式。
配置sqlMapConfig.xml
<typeAliases>
<typeAlias type="com.duoduo.pojo.User" alias="user"></typeAlias>
</typeAliases>
修改映射文件UserDao.xml,使用别名替换
<select id="queryAllUser" resultType="user">
select * from user
</select>
另外一种写法
<typeAliases>
<!--<typeAlias type="com.xq.pojo.User" alias="user"></typeAlias>-->
<!--
在当前包下的所有类的别名均设置为类名的小写
-->
<package name="com.duoduo.pojo"/>
</typeAliases>
Mappers标签
Mappers标签里面定义mapper标签。作用是用来在核心配置文件里面引入映射文件。
第一种:
<mappers>
<mapper resource="com/duoduo/dao/UserDao.xml"></mapper>
</mappers>
第二种:
使用 mapper 接口类路径。如果我们使用注解开发的时候,就需要使用这个路径
<mappers>
<!--路径层级之间用 / 拼接-->
<mapper class="com.xq.dao.UserDao"></mapper>
</mappers>
第三种:
<mappers>
<!--<mapper resource="com/duoduo/dao/UserDao.xml"></mapper>-->
<!--路径层级之间用 . 拼接-->
<package name="com.duoduo.dao"/>
</mappers>
动态SQL
动态查询(if标签 、where标签)
数据控制层Dao
public interface UserDao {
List<User> queryUserByLive(User user);
List<User> queryUserByLive2(User user);
}
UserDao.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">
<mapper namespace="com.duoduo.dao.UserDao">
<select id="queryUserByLive" resultType="user" parameterType="user">
select * from user where 1 = 1
<if test="id != null and id != ''">
and id = #{id}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="birthday != null and birthday != ''">
and birthday = #{birthday}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
<if test="address != null and address != ''">
and address = #{address}
</if>
</select>
<select id="queryUserByLive2" resultType="user" parameterType="user">
select * from user
<!--
where 标签
替换了where 1 = 1
where标签本身解析成sql语句中的where关键字
并且在底层解析sql的时候,满足条件的第一个if标签,会将if里面的and给去掉
-->
<where>
<if test="id != null and id != ''">
and id = #{id}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="birthday != null and birthday != ''">
and birthday = #{birthday}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
<if test="address != null and address != ''">
and address = #{address}
</if>
</where>
</select>
</mapper>
测试类:
public void testLiveQuery(){
User user = new User();
user.setUsername("tom");
user.setSex("男");
user.setAddress("广东深圳");
List<User> users = userDao.queryUserByLive(user);
users.forEach(u -> System.out.println(u));
}
public void testLiveQuery2(){
User user = new User();
user.setUsername("tom");
user.setSex("男");
user.setAddress("广东深圳");
List<User> users = userDao.queryUserByLive2(user);
users.forEach(u -> System.out.println(u));
}
总结:
where 标签
替换了where 1 = 1
where标签本身解析成sql语句中的where关键字
并且在底层解析sql的时候,满足条件的第一个if标签,会将if里面的and给去掉
动态查询(where choose when otherwise 标签)
数据控制层Dao
List<User> queryUserByLive3(User user);
UserDao.xml 配置文件的编写
<select id="queryUserByLive3" resultType="user" parameterType="user">
select * from user
<where>
<choose>
<when test="username != null and username != '' ">
username = #{username}
</when>
<when test="birthday != null and birthday != '' ">
birthday = #{birthday}
</when>
<when test="sex != null and sex != '' ">
sex = #{sex}
</when>
<when test="address != null and address != '' ">
address = #{address}
</when>
<otherwise>
id = #{id}
</otherwise>
</choose>
</where>
</select>
测试类:
public void testLiveQuery3(){
User user = new User();
user.setUsername("tom");
user.setSex("男");
user.setAddress("广东深圳");
List<User> users = userDao.queryUserByLive3(user);
users.forEach(u -> System.out.println(u));
}
public void testLiveQuery3(){
User user = new User();
user.setId(52);
List<User> users = userDao.queryUserByLive3(user);
users.forEach(u -> System.out.println(u));
}
总结:
类似于Java语言中的
if(条件1){
....
} else if(条件2){
....
}else if(条件3){
....
} else {
....
}
choose
when 标签 :当条件满足当前标签,将不再向下执行
otherwise 标签: 当前面所有when标签的条件都不满足时,执行otherwise标签的内容
动态修改(set标签)
数据控制层Dao
void updateUserByLive(User user);
UserDao.xml 配置文件的编写
<update id="updateUserByLive" parameterType="user">
update user
<!--
set 标签
set 标签自动解析成了sql语句的set关键字
并且底层在解析set的时候,将最后一个if中的内容的逗号(,)去除,以保证sql语句执行的正确性。
-->
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="birthday != null and birthday != ''">
birthday = #{birthday},
</if>
<if test="sex != null and sex != ''">
sex = #{sex},
</if>
<if test="address != null and address != ''">
address = #{address},
</if>
</set>
where id = #{id}
</update>
测试类:
public void testLiveUpdate(){
User user = new User();
user.setUsername("tom");
user.setSex("男");
user.setAddress("广东深圳");
userDao.updateUserByLive(user);
}
动态新增(sql标签 trim标签 include标签)
数据控制层Dao
void insertUserByLive(User user);
UserDao.xml 配置文件的编写
<sql id="insertUserKey">
<trim suffixOverrides=",">
<if test="username != null and username != '' ">
username,
</if>
<if test="birthday != null and birthday != '' ">
birthday,
</if>
<if test="sex != null and sex != '' ">
sex,
</if>
<if test="address != null and address != '' ">
address,
</if>
</trim>
</sql>
<sql id="insertUserValue">
<trim suffixOverrides=",">
<if test="username != null and username != '' ">
#{username},
</if>
<if test="birthday != null and birthday != '' ">
#{birthday},
</if>
<if test="sex != null and sex != '' ">
#{sex},
</if>
<if test="address != null and address != '' ">
#{address},
</if>
</trim>
</sql>
<insert id="insertUserByLive" parameterType="user">
insert into user(<include refid="insertUserKey"></include>) values (<include refid="insertUserValue"></include>)
</insert>
测试结果:
※批处理
新增
数据持久层Dao
void insertUsers(User user);
UserDao.xml 配置文件的编写
<insert id="insertUsers" parameterType="user">
insert into user(username, birthday, sex, address) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
测试类:
public void testInsertUsers(){
List<User> list = new ArrayList<>();
User user1 = new User();
user1.setUsername("测试数据1");
user1.setBirthday(new Date());
user1.setSex("N");
user1.setAddress("测试数据1");
User user2 = new User();
user2.setUsername("测试数据2");
user2.setBirthday(new Date());
user2.setSex("N");
user2.setAddress("测试数据2");
User user3 = new User();
user3.setUsername("测试数据3");
user3.setBirthday(new Date());
user3.setSex("N");
user3.setAddress("测试数据3");
Collections.addAll(list,user1,user2,user3);
list.forEach(user -> userDao.insertUsers(user));
}
当未开启mybatis批处理的情况下,sql语句会预编译三次
开启批处理
注意!!!:开启批处理以后,要手动进行事务的提交
开启方式1:
在mybatis核心配置文件中添加标签 并设置参数 name="defaultExecutorType" value="BATCH"
<!--开启批处理-->
<settings>
<setting name="defaultExecutorType" value="BATCH"/>
</settings>
开启方式2:
在创建SqlSession对象的时候,指定开启批处理
SqlSession session = factory.openSession(ExecutorType.BATCH,false);
多表查询
一对一查询
一个账户对应着一个用户,在Account类中维护一个属性User user
@Getter
@Setter
@ToString
public class Account {
private Integer aid;
private Integer uid;
private Double money;
//维护一个实体User属性
private User user;
}
数据持久层Dao
List<Account> queryAccountAndUser();
UserDao.xml 配置文件的编写
一对一的实体维护:association 标签 内部属性是 javaType
<resultMap id="accountUserMap" type="account">
<id column="aid" property="aid"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
<association property="user" javaType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
</association>
</resultMap>
<select id="queryAccountAndUser" resultMap="accountUserMap">
select account.AID, account.UID, account.MONEY,user.id,user.username,user.address from account,user where UID = user.id;
</select>
测试类:
public void testQueryAccountUser(){
List<Account> accounts = accountDao.queryAccountAndUser();
accounts.forEach(account -> System.out.println(account));
}
一对多查询
一个用户对应多个账户,在User中添加一个List 对accounts进行维护
@Data
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
}
数据持久层Dao
List<User> queryUserAndAccount();
UserDao.xml 配置文件的编写
一对多的实体维护:collection 标签,内部属性是ofType(写集合的范型)
<resultMap id="userAccountMap" type="com.duoduo.pojo.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<collection property="accounts" ofType="account">
<id column="aid" property="aid"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<select id="queryUserAndAccount" resultMap="userAccountMap">
select user.id, username, birthday, sex, address,account.MONEY from user,account where account.UID = user.id;
</select>
测试类:
public void testQueryAccountUser(){
List<User> users = userDao.queryUserAndAccount();
users.forEach(user -> System.out.println(user));
}
多对多查询
@Data
public class Role {
private Integer roleId;
private String roleName;
private String roleDesc;
private List<User> users;
}
数据持久层Dao
List<Role> queryRoleAndUser();
UserDao.xml 配置文件的编写
和一对多好像是一样的
<resultMap id="roleMap" type="role">
<id column="rid" property="roleId"></id>
<result column="role_name" property="roleName"></result>
<result column="role_desc" property="roleDesc"></result>
<collection property="users" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="birthday" property="birthday"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
</collection>
</resultMap>
<select id="queryRoleAndUser" resultMap="roleMap">
select role.*,user.username,user.address
from role,user_role,user
where role.RID = user_role.RID and user.id = user_role.UID
</select>
测试类:
public void testQueryAccountUser(){
List<Role> roles = roleDao.queryRoleAndUser();
roles.forEach(role -> System.out.println(role));
}
Mybatis延时加载策略
Account
@Getter
@Setter
@ToString
public class Account {
private Integer aid;
private Integer uid;
private Double money;
private User user;
}
User
@Data
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
AccountDao
public interface AccountDao {
List<Account> queryAllAccount();
}
UserDao
public interface UserDao {
List<User> queryUserById(Integer id);
}
AccountDao.xml
<resultMap id="accountMap" type="account">
<id column="aid" property="aid"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
<association property="user" column="uid" select="com.duoduo.dao.UserDao.queryUserById"></association>
</resultMap>
<select id="queryAllAccount" resultMap="accountMap">
select * from account
</select>
UserDao.xml
<select id="queryUserById" resultType="user" parameterType="int">
select * from user where id = #{id}
</select>
测试类:
@Test
public void testQueryAccountUser(){
List<Account> accounts = accountDao.queryAllAccount();
//accounts.forEach(account -> System.out.println(account));
}
执行了三条sql语句并没有延时加载的效果
延时加载配置
方式一:mybatis主要配置文件
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<!--
mybatis3.4.1版本后可以不写,默认为false。
-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
方式二:AccountDao.xml文件配置
在association标签内添加一个fetchType属性 值为lazy即可做到懒加载
<resultMap id="accountMap" type="account">
<id column="aid" property="aid"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
<association property="user" column="uid" select="com.duoduo.dao.UserDao.queryUserById" fetchType="lazy"></association>
</resultMap>
<select id="queryAllAccount" resultMap="accountMap">
select * from account
</select>
测试类:
一定要把forEach语句注释掉
@Test
public void testQueryAccountUser(){
List<Account> accounts = accountDao.queryAllAccount();
//accounts.forEach(account -> System.out.println(account));
}
Mybatis缓存
-
什么是缓存
- 缓存就是存在内存中的数据
-
为什么使用缓存
-
减少和数据库交互的次数,提高执行效率
因为数据库中的数据文件本质上其实是存储在硬盘上的(加密)文件,所以从内存中查找是要比从硬盘中查询是要块的
-
-
什么样的数据适合使用缓存,什么样得数据不适合使用缓存
- 适用:
- 经常查询的数据,且不经常改变的数据(账号密码的存储)
- 数据的正确与否队最终结果影响不大的
- 不适用:
- 经常改变的,数据的正确与否堆结果影响很大的。比如:商品的库存,银行的汇率、股市的股价。
- 适用:
一级缓存
他指的是mybatis中SqlSession对象的缓存。当我们执行完查询之后,查询的结果会同时存在SqlSession为我们提供的一块区域中,该区域的结构是一个Map。
当我们再次查询同样的结果时,mybatis会先去SqlSession中查询是否存在,存在则直接拿出来。当SqlSession对象消失时,mybatis的一级缓存也就消失了。sqlSession.close();
数据持久层Dao
User queryUserById(Integer id);
映射文件的编写UserDao.xml
<select id="queryUserById" resultType="user" parameterType="int">
select * from user where id = #{id}
</select>
测试类:
public void testCache(){
User user1 = userDao.queryUserById(48);
System.out.println("The first query:" + user1);
User user2 = userDao.queryUserById(48);
System.out.println("The first query:" + user2);
}
这里我们可以发现,虽在我们调用了两次userDao的QueryUserById方法,但是只执行了一次数据库的交互,这就是mybatis的一级缓存起作用了,因为一级缓存的存在,所以第二次查询时直接从一级缓存中去取,没有再进行数据库的二次交互。
如果SqlSeesion执行了commit操作(更新)或者关闭,会清空缓存中的信息,避免脏读
清空缓存(sqlSqssion.close)
public void testCache(){
User user1 = userDao.queryUserById(48);
System.out.println("The first query:" + user1);
session.close(); //清空缓存
SqlSession session = factory.openSession();
userDao = session.getMapper(UserDao.class);
User user2 = this.userDao.queryUserById(48);
System.out.println("The first query:" + user2);
}
数据发生更新(update)
public void testCache(){
User user1 = userDao.queryUserById(48);
System.out.println("The first query:" + user1);
User user = new User();
user.setId(48);
user.setUsername("小爱同学");
user.setSex("女");
user.setBirthday(new Date());
user.setAddress("广东深圳");
userDao.updateUser(user);
session.commit();
User user2 = this.userDao.queryUserById(48);
System.out.println("The second query:" + user2);
}
数据发生变换,update会直接抹除当前数据在缓存中的数据,避免脏读
二级缓存
sqlSessionFactory的缓存
二级缓存的开启:
pojo类必须实现Serializable接口
4.2.3.1 测试二级缓存
- 第一步 在SqlMapConfig.xml 文件开启二级缓存
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。为true代表开启二级缓存;为false代表不开启二级缓存。
- 第二步 配置相关的Mapper映射文件
<mapper namespace="com.xq.dao.UserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
- 第三步 配置statement 上面的useCache属性
<select id="findUserById" resultType="user" parameterType="int" useCache="true">
select id,username,birthday,sex ,address from user where id=#{id}
</select>
总结:
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存
Mybatis注解开发
打开mybatis核心配置文件中的包映射
<mappers>
<package name="com.duoduo.dao"/>
</mappers>
数据控制层Dao
//查询所有
@Select("select * from user")
List<User> queryAllUser();
//通过id查询
@Select("select * from user where id = #{id}")
User queryUserById(Integer id);
/**
*添加操作
*@SelectKey注解:查询表中最后新增数据的主键
* keyProperty:实体属性的名称
* keyColumn: 数据表对应字段的名称
* resultType: 属性对应的数据类型
* before: 代表查询的时机,false是在新增之后
* statement: {查询语句}
*/
@Insert("insert into user(username, birthday, sex, address) VALUES (#{username},#{birthday},#{sex},#{address})")
@SelectKey(keyProperty = "id",keyColumn = "id",resultType = Integer.class,before = false,statement = {"select 240308"})
void insertUser(User user);
//更新操作的注解
@Update("update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}")
void updateUser(User user);
//删除的实现
@Delete("delete from user where id = #{id}")
void deleteUserById(Integer id);
//实现聚合函数的查询
@Select("select count(id) from user")
Integer getCount();
//实现模糊查询
@Select("select * from user where username like #{username}")
List<User> queryDimByUsername(String username);
Mybatis注解解决前后字段不一致的问题
//id 为resultMap引用的id,唯一
@Results(id = "roleMap",value = {
//id字段默认为false ,id = true 代表当前字段是主键字段
@Result(id = true,column = "rid",property = "rid"),
@Result(column = "role_name",property = "roleName"),
@Result(column = "role_desc",property = "roleDesc")
})
@Select("select * from role")
List<Role> queryAllRole();
//多次使用直接引用@Results的id
@ResultMap("roleMap")
@Select("select * from role where RID = #{id}")
Role queryRoleByRid(Integer id);
基于注解多表查询一对一查询
在Account类里面维护一个pojo实体user
@Data
public class Account {
private Integer aid;
private Integer uid;
private Double money;
private User user;
}
数据持久层Dao
public interface UserDao {
@Select("select * from user where id = #{id}")
User queryUserById();
}
public interface AccountDao {
/**
* 一对一用 one = @one
* 参数:select
* fetchType :加载类型 FetchType.LAZY 懒加载
* @return
*/
@Results(id = "accountMap",value = {
@Result(id = true,column = "aid",property = "aid"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(column = "uid",property = "user",
one = @One(select = "com.duoduo.dao.UserDao.queryUserById",
fetchType = FetchType.LAZY)
)
})
@Select("select * from account")
List<Account> queryAllAccount();
}
基于注解多表查询一对多查询
在user类维护一个account集合
@Data
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
}
数据持久层Dao
/**
* 一对多用 many = @Many
* 参数:select
* fetchType :加载类型 FetchType.LAZY 懒加载
* @return
*/
@Results(id = "userMap",value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
@Result(column = "id",property = "accounts",
many = @Many(select = "com.duoduo.dao.AccountDao.queryAccountByUid",
fetchType = FetchType.LAZY)
)
})
@Select("select * from user")
List<User> queryAllUser();
@Select("select * from account where UID = #{uid}")
List<Account> queryAccountByUid(Integer uid);
基于注解多表查询多对多查询
在Role类维护一个user集合
@Data
public class Role {
private Integer rid;
private String roleName;
private String roleDesc;
private List<User> users;
}
数据持久层Dao
@Results(id = "roleMap",value = {
@Result(id = true,column = "rid",property = "rid"),
@Result(column = "role_name",property = "roleName"),
@Result(column = "role_desc",property = "roleDesc"),
@Result(column = "rid",property = "users",
many = @Many(select = "com.duoduo.dao.UserDao.queryUsersById",
fetchType = FetchType.LAZY)
)
})
@Select("select * from role")
List<Role> queryAllRole();
@Select("select * from user where id in(select uid from user_role where RID = #{rid})")
List<User> queryUsersById(Integer id);
Mybatis逆向工程的搭建
导入插件:
注意mysql驱动版本要和依赖中的一致
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
创建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">
<configuration>
<properties resource="db.properties"></properties>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
</environment>
</environments>
</configuration>
创建逆向工程的配置文件
注意:逆向工程的文件名必须是:generatorConfig.xml
MyBatis3Simple: 生成基本的CRUD MyBatis3: 生成带条件的CRUD
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD
MyBatis3: 生成带条件的CRUD
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=UTF-8&useUnicode=true"
userId="root"
password="rootroot">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.duoduo.pojo" targetProject=".\src\main\java">
<!--
是否生成子包。如果为true com.xq.pojo生成的保姆那个带有层级目录
false com.xq.pojo就是一个包名
-->
<property name="enableSubPackages" value="true" />
<!--
通过数据表字段生成pojo。如果字段名称带空格,会去掉空格
-->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.duoduo.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.duoduo.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="user" domainObjectName="User"/>
</context>
</generatorConfiguration>
执行后的效果
Mybatis分页插件
导入依赖
<!--分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
在mybatis核心配置文件中导入插件
<!--配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
测试类:
@Test
public void test03() throws Exception{
//开启分页操作
PageHelper.startPage(2,4);
List<User> userList = userMapper.selectByExample(null);
//创建分页模型对象
PageInfo<User> pageInfo = new PageInfo<>(userList);
System.out.println(pageInfo);
}
属性解读:
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]