Mybatis
ref:
https://www.bilibili.com/video/BV1VP4y1c7j7
获取
一、Github下载源码与官方文档
👉 GitHub - mybatis -> README -> Essentials -> Download Latest -> assets -> mybatis-3.5.11.zip
二、Maven引入
<dependency >
<groupId > org.mybatis</groupId >
<artifactId > mybatis</artifactId >
<version > 3.5.9</version >
</dependency >
起步
添加依赖
配置mybatis-config.xml
transactionManager 事务管理方式:JDBC
dataSource 连接数据库信息
POOLED 使用数据库连接池(缓存)
mapper 接口映射xml文档位置(包下用".",resources下用"/")
<?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 defalut ="development" >
<environment id ="development" >
<transactionManager type ="JDBC" />
<dataSource type ="POOLED" >
<property name ="driver" value ="com.mysql.cj.jdbc.Driver" />
<property name ="url" value ="jdbc:mysql://localhost:3306/xxx" />
<property name ="username" value ="xxx" />
<property name ="password" value ="xxx" />
</dataSource >
</environment >
</environments >
<mappers >
<mapper resource ="mappers/UserMapper.xml" />
</mappers >
</configuration >
示例
com.xxx.mybatis.model创建User类,属性与表一致
com.xxx.mybatis.mapper创建UserMapper接口
resources/mappers创建UserMapper.xml。namespace值为对应接口全名,id值为接口方法名
User
public class User {
private String username;
private String password;
private Integer roleid;
}
UserMapper
public interface UserMapper {
int insertUser () ;
}
UserMapper.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.learn.mybatis.mapper.UserMapper" >
<insert id ="insertUser" >
insert into t_user values(null,'zhangsan','123456',1)
</insert >
</mapper >
最后需要在mybatis-config.xml配置文件中引入
</configuration >
...
<mappers >
<mapper resource ="mappers/UserMapper.xml" />
</mappers >
</configuration >
测试
test/java
com.xxx.mybatis.test
使用字节输入流获取配置文件,传入工厂构建器实例构建得到工厂对象,工厂获取会话,通过会话获取对应接口实例。
public class MyBatisTest {
@Test
public void testMyBatis () throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml" );
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder ();
SqlSessionFactory ssf = ssfb.build(is);
SqlSession sqlSession = ssf.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int b = mapper.insertUser();
sqlSession.commit();
System.out.println("insertUser: " + b);
}
}
事务自动提交
SqlSession sqlSession = ssf.openSession(true );
openSessions的源码
public SqlSession openSession (boolean autoCommit) {
return this .sqlSessionFactory.openSession(autoCommit);
}
引入log4j
级别:fatal(致命) > error(错误) > warn > info > debug
设置等级为debug,所有信息将被打印(error、info等)
pom.xml
<dependency >
<groupId > log4j</groupId >
<artifactId > log4j</artifactId >
<version > 1.2.17</version >
</dependency >
resources/log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j :configuration SYSTEM "log4j.dtd" >
<log4j:configuration xmlns:log4j ="http://jakarta.apache.org/log4j/" >
<appender name ="STDOUT" class ="org.apache.log4j.ConsoleAppender" >
<param name ="Encoding" value ="UTF-8" />
<layout class ="org.apache.log4j.PatternLayout" >
<param name ="ConversionPattern" value ="%-5p %d{MM-dd HH:mm:ss,SSS} %m(%F:%L) \n" />
</layout >
</appender >
<logger name ="java.sql" >
<level value ="debug" />
</logger >
<logger name ="org.apache.ibatis" >
<level value ="info" />
</logger >
<root >
<level value ="debug" />
<appender-ref ref ="STDOUT" />
</root >
</log4j:configuration >
运行示例
DEBUG 11 -02 04 :17 :29 ,630 ==> Preparing: insert into t_user values(null,'zhangsan' ,'123456' ,1 )(BaseJdbcLogger.java:137)
DEBUG 11 -02 04 :17 :29 ,674 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 11 -02 04 :17 :29 ,676 <== Updates: 1 (BaseJdbcLogger.java:137)
insertUser: 1
Process finished with exit code 0
*Mapper.xml
namespace 值为对应接口全类名
insert 等标签的 id 值为接口的方法名
select查询标签需要返回类型resultType(类属性名与数据库字段名一致时)或resultMap(类属性名与数据库字段名不一致时,如类的id实际上是表的t_id,需要自定义映射关系)
mybatis-config.xml
environments 配置多个连接数据库的环境,属性default表示默认环境(对应环境id值)
environment 具体环境,以id为标识
transactionManager 事务管理方式 JDBC|MANAGED ,JDBC表示原生JDBC事务管理方式,MANAGED表示被谁管理,如Spring
dataSource 数据源 POOLED表示使用连接池(缓存),UNPOOLED表示不使用连接池,JNDI表示使用上下文数据源
settings mybatis全局配置
标签有先后顺序要求
元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"
<settings >
<setting name ="mapUnderscoreToCamelCase" value ="true" />
<setting name ="lazyLoadingEnabled" value ="true" />
</settings >
<mappers >
<package name ="com.xxx.mybatis.mapper" />
</mappers >
<typeAliases >
<typeAlias type ="com.learn.mybatis.model.User" alias ="User" />
<package name ="com.learn.mybatis.model" />
</typeAliases >
jdbc.properties
resources/mybatis-config.xml
<properties resource ="jdbc.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 >
resources/jdbc.properties
jdbc.driver =com.mysql.cj.jdbc.Driver
jdbc.url =jdbc:mysql://localhost:3306/xxx?useUnicode=true&charsetEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username =xxx
jdbc.password =xxx
模板设置
settings > Editor > File and CodeTemplates
获取参数值
单个参数时,参数名是什么无关紧要,但是一般与参数名保持一致
多个参数时,需要一一对应,否则不知道匹配哪一个。参数存储在map中
例:
<update id ="updateUser" >
update t_user set username = '张三' where id = '${aaa}'
</update >
update t_user set username = '张三' where username = 'zhangsan'
例
<update id ="updateUser" >
update t_user set username = '张三' where id = #{aaa}
</update >
此时日志输出
update t_user set username = '张三' where username = ?
parameters: zhangsan(String)
接口参数为对象
User checkLoginByMap (Map<String, Object> map) ;
<select id ="checkLoginByMap" resultType ="User" >
select * from t_user where username = #{username} and password = #{password}
</select >
Map<String,Object> map = new HashMap <>();
map.put("username" , "111" );
map.put("password" , "1111" );
User user = mapper.checkLoginByMap(map);
int insertUser (User user) ;
<insert id ="insertUser" >
insert into t_user values(null,#{username},#{password},#{roleId})
</insert >
mapper.insertUser(new User (null , "111" , "1111" , 1 ));
User checkLoginByMap (@Param("uname") String username, @Param("upwd") String password) ;
<select id ="checkLoginByParam" resultType ="User" >
select * from t_user where username = #{uname} and password = #{upwd}
</select >
mapper.checkLoginByParam("111" , "1111" );
多种查询
User getUserById (@Param("id") Integer id) ;
<select id ="getUserById" resultType ="User" >
select * from t_user where id = #{id}
</select >
<select id ="getAllUser" resultType ="User" >
select * from t_user
</select >
<select id ="getUserCount" resultType ="java.lang.Long" >
select count(*) from t_user
</select >
Long userCount = mapper.getUserCount();
单条数据返回map(场景,查询到的结果没有相应的实体接收,如多表查询)
Map<String,Object> getUserByIdToMap (@Param("id") Integer id) ;
<select id ="getUserByIdToMap" resultType ="map" >
select * from t_user where id = #{id}
</select >
Map<String, Object> map = mapper.getUserByIdToMap(1 );
List<Map<String,Object>> getAllUserToMap () ;
<select id ="getAllUserToMap" resultType ="map" >
select * from t_user
</select >
List<Map<String, Object>> list = mapper.getAllUserToMap();
使用@MapKey(id作为Map的键,对象内容作为值)
@MapKey("id")
Map<String,Object> getAllUserToMap () ;
<select id ="getAllUserToMap" resultType ="map" >
select * from t_user
</select >
Map<String, Object> map = mapper.getAllUserToMap();
'%${username}%' | concat('%',#{username},'%') | "%"#{username}"%"
User getUserByLike (@Param("username") String username) ;
<select id ="getUserByLike" resultType ="User" >
select * from t_user where username like '%${username}%'
</select >
<select id ="getUserByLike" resultType ="User" >
select * from t_user where username like concat('%',#{username},'%')
</select >
<select id ="getUserByLike" resultType ="User" >
select * from t_user where username like "%"#{username}"%"
</select >
User user = mapper.getUserByLike("2" );
${tableName}
List<User> getAllUserByTableName (@Param("tableName") String tableName) ;
<select id ="getAllUserByTableName" resultType ="User" >
select * from ${tableName}
</select >
List<User> t_user = mapper.getAllUserByTableName("t_user" );
批量删除
org.apache.ibatis.exceptions.PersistenceException:
Integer deleteMore (@Param("ids") String ids) ;
<delete id ="deleteMore" >
delete from t_user where id in (${ids})
</delete >
int i = mapper.deleteMore("6,7" );
获取自增主键
useGeneratedKeys: 设置当前sql使用了自动递增的主键
keyProperty:将自增的主键的值赋值给参数的某个属性
void insertUserGetKey (User user) ;
<insert id ="insertUserGetKey" useGeneratedKeys ="true" keyProperty ="id" >
insert into t_user values (null,#{username},#{password},#{roleId})
</insert >
User user = new User (null , "张三666" , "123456" , 1 );
mapper.insertUserGetKey(user);
类属性名与列名保持一致
public class User {
private String tName;
}
select t_name as tName from t_user
<settings >
<setting name ="mapUnderscoreToCamelCase" value ="true" />
</settings >
标签id :主键映射关系
标签result :普通字段映射关系
association :处理多对一映射关系 n:1
collection :处理一对多映射关系 1:n
public class Role {
private Integer rId;
private String rName;
private String rRemark;
}
<select id ="getAllRole" resultMap ="roleResultMap" >
select * from t_role
</select >
<resultMap id ="roleResultMap" type ="Role" >
<id property ="rId" column ="r_id" />
<result property ="rName" column ="r_name" />
<result property ="rRemark" column ="r_remark" />
</resultMap >
处理n:1映射关系
role.rId : role表示User类中Role的变量名role,rId 表示role的属性名
public class User {
private Integer id;
private String username;
private String password;
private Integer roleId;
private Role role;
}
User getUserAndRoleById (@Param("id") Integer id) ;
<select id ="getUserAndRoleById" resultMap ="userAndRole" >
SELECT * FROM t_user u LEFT JOIN t_role r on u.roleid = r.r_id WHERE u.id = 1
</select >
<resultMap id ="userAndRole" type ="User" >
<id property ="id" column ="id" />
<result property ="username" column ="username" />
<result property ="password" column ="password" />
<result property ="roleId" column ="roleid" />
<result property ="role.rId" column ="r_id" />
<result property ="role.rName" column ="r_name" />
<result property ="role.rRemark" column ="r_remark" />
</resultMap >
User user = mapper.getUserAndRoleById(1 );
解释:将查询到的字段r_id映射到类型Role变量名role的属性rId
<select id ="getUserAndRoleById" resultMap ="userAndRole" >
SELECT * FROM t_user u LEFT JOIN t_role r on u.roleid = r.r_id WHERE u.id = 1
</select >
<resultMap id ="userAndRole" type ="User" >
<id property ="id" column ="id" />
<result property ="username" column ="username" />
<result property ="password" column ="password" />
<result property ="roleId" column ="roleid" />
<association property ="role" javaType ="Role" >
<id property ="rId" column ="r_id" />
<result property ="rName" column ="r_name" />
<result property ="rRemark" column ="r_remark" />
</association >
</resultMap >
第一步,根据id查询用户
查询到的column="roleid"(roleid列)的值给select传递过去,作为com.learn.mybatis.mapper.RoleMapper.getRole的参数
User getUserAndRoleById (@Param("id") Integer id) ;
<select id ="getUserAndRoleById" resultMap ="userAndRole" >
SELECT * FROM t_user where id = #{id}
</select >
<resultMap id ="userAndRole" type ="User" >
<id property ="id" column ="id" />
<result property ="username" column ="username" />
<result property ="password" column ="password" />
<result property ="roleId" column ="roleid" />
<association property ="role"
column ="roleid"
select ="com.learn.mybatis.mapper.RoleMapper.getRole" >
</association >
</resultMap >
第二步,查询Role,这里可以拿到id信息,其值为第一步查询roleid列的值
Role getRole (@Param("id") Integer id) ;
<select id ="getRole" resultType ="Role" >
select * from t_role where r_id = #{id}
</select >
日志
DEBUG 11 -07 13 :46 :36 ,206 ==> Preparing: SELECT * FROM t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 13 :46 :36 ,244 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 13 :46 :36 ,273 ====> Preparing: select * from t_role where r_id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 13 :46 :36 ,274 ====> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 13 :46 :36 ,277 <==== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 11 -07 13 :46 :36 ,280 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1 , username='zhangsan1' , password='123' , roleId=1 , role=Role{rId=1 , rName='普通用户' , rRemark='用户中心' }}
处理1:n映射关系
public class Role {
private Integer rId;
private String rName;
private String rRemark;
private List<User> userList;
}
Role getRoleAndUser (@Param("rId") Integer rId) ;
<select id ="getRoleAndUser" resultMap ="userAndRoleMap" >
SELECT * FROM t_role r LEFT JOIN t_user u on r.r_id = u.roleid WHERE r.r_id = #{rId}
</select >
<resultMap id ="userAndRoleMap" type ="Role" >
<id property ="rId" column ="r_id" />
<result property ="rName" column ="r_name" />
<result property ="rRemark" column ="r_remark" />
<collection property ="userList" ofType ="User" >
<id property ="id" column ="id" />
<result property ="username" column ="username" />
<result property ="password" column ="password" />
<result property ="roleId" column ="roleid" />
</collection >
</resultMap >
Role role = mapper.getRoleAndUser(1 );
System.out.println(role);
第一步
Role getRoleAndUserStepOne (@Param("rId") Integer rId) ;
<select id ="getRoleAndUserStepOne" resultMap ="getRoleAndUserStepOneMap" >
select * from t_role where r_id = #{rId}
</select >
<resultMap id ="getRoleAndUserStepOneMap" type ="Role" >
<id property ="rId" column ="r_id" />
<result property ="rName" column ="r_name" />
<result property ="rRemark" column ="r_remark" />
<collection property ="userList"
select ="com.learn.mybatis.mapper.UserMapper.getRoleAndUserStepTwo"
column ="r_id" />
</resultMap >
第二步
List<User> getRoleAndUserStepTwo (@Param("id") Integer id) ;
<select id ="getRoleAndUserStepTwo" resultType ="User" >
select * from t_user where id = #{id}
</select >
日志
Role role = mapper.getRoleAndUserStepOne(1 );
System.out.println(role);
DEBUG 11 -07 16 :10 :54 ,533 ==> Preparing: select * from t_role where r_id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 16 :10 :54 ,566 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 16 :10 :54 ,595 ====> Preparing: select * from t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 16 :10 :54 ,596 ====> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 16 :10 :54 ,598 <==== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 11 -07 16 :10 :54 ,601 <== Total: 1 (BaseJdbcLogger.java:137)
Role{rId=1 , rName='普通用户' , rRemark='用户中心' , userList=[User{id=1 , username='zhangsan1' , password='123' , roleId=1 , role=null}]}
延迟加载
全局开启
<settings >
<setting name ="lazyLoadingEnabled" value ="true" />
</settings >
示例一
User user = mapper.getUserAndRoleById(1 );
System.out.println(user.getUsername());
lazyLoadingEnabled 不同值时运行效果
lazyLoadingEnabled = true(有访问才会执行)
DEBUG 11 -07 14 :00 :24 ,073 ==> Preparing: SELECT * FROM t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :00 :24 ,112 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :00 :24 ,194 <== Total: 1 (BaseJdbcLogger.java:137)
zhangsan1
lazyLoadingEnabled = false(没访问不执行对应SQL)
DEBUG 11 -07 14 :02 :06 ,382 ==> Preparing: SELECT * FROM t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :02 :06 ,421 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :02 :06 ,453 ====> Preparing: select * from t_role where r_id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :02 :06 ,453 ====> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :02 :06 ,456 <==== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :02 :06 ,459 <== Total: 1 (BaseJdbcLogger.java:137)
zhangsan1
示例二
User user = mapper.getUserAndRoleById(1 );
System.out.println(user.getUsername());
System.out.println(user.getRole());
lazyLoadingEnabled = true
DEBUG 11 -07 14 :05 :18 ,478 ==> Preparing: SELECT * FROM t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :05 :18 ,513 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :05 :18 ,593 <== Total: 1 (BaseJdbcLogger.java:137)
zhangsan1
DEBUG 11 -07 14 :05 :18 ,595 ==> Preparing: select * from t_role where r_id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :05 :18 ,596 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :05 :18 ,598 <== Total: 1 (BaseJdbcLogger.java:137)
Role{rId=1 , rName='普通用户' , rRemark='用户中心' }
lazyLoadingEnabled = false
DEBUG 11 -07 14 :08 :22 ,557 ==> Preparing: SELECT * FROM t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :08 :22 ,593 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :08 :22 ,620 ====> Preparing: select * from t_role where r_id = ?(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :08 :22 ,621 ====> Parameters: 1 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :08 :22 ,623 <==== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 11 -07 14 :08 :22 ,626 <== Total: 1 (BaseJdbcLogger.java:137)
zhangsan1
Role{rId=1 , rName='普通用户' , rRemark='用户中心' }
单个开启
fetchType="lazy | eager" 优先级高于全局配置
开启(延迟执行) fetchType="lazy"
<association property ="role"
column ="roleid"
fetchType ="lazy"
select ="com.learn.mybatis.mapper.RoleMapper.getRole" >
</association >
关闭(立即执行) fetchType="eager
<association property ="role"
column ="roleid"
fetchType ="eager"
select ="com.learn.mybatis.mapper.RoleMapper.getRole" >
</association >
动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题 。
if
List<User> getUserByCondition (User user) ;
<select id ="getUserByCondition" resultType ="User" >
select * from t_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 ="password != null and password != ''" >
and password = #{password}
</if >
<if test ="roleId != null and roleId != ''" >
and roleid = #{roleId}
</if >
</select >
where
自动添加where关键字,自动处理where里面的内容
会自动去掉 if 标签内容位于前面 多余的and和or等
<select id ="getUserByCondition" resultType ="User" >
select * from t_user
<where >
<if test ="id != null and id != ''" >
id = #{id}
</if >
<if test ="username != null and username != ''" >
and username = #{username}
</if >
<if test ="password != null and password != ''" >
or password = #{password}
</if >
<if test ="roleId != null and roleId != ''" >
and roleid = #{roleId}
</if >
</where >
</select >
trim
prefix|suffix : 在trim标签前面或者后面添加 指定内容
prefixOverrides|suffixOverrides : 在trim标签前面或者后面去掉 指定内容
< trim prefix = "where" suffixOverrides= "and|or" > 在前面添加where ,去掉后面的and或者or
<select id ="getUserByCondition" resultType ="User" >
select * from t_user
<trim prefix ="where" suffixOverrides ="and|or" >
<if test ="id != null and id != ''" >
id = #{id} and
</if >
<if test ="username != null and username != ''" >
username = #{username} and
</if >
<if test ="password != null and password != ''" >
password = #{password} and
</if >
<if test ="roleId != null and roleId != ''" >
roleid = #{roleId} and
</if >
</trim >
</select >
choose、when、otherwise
相当于Java的if...else if...else...
满足任意一个when时,立即停止后面的when判断。当所有的when都不满足,使用otherwise的内容
when至少一个
otherwise至多一个
<select id ="getUserByCondition" resultType ="User" >
select * from t_user
<where >
<choose >
<when test ="id != null and id != ''" >
id = #{id}
</when >
<when test ="username != null and username != ''" >
username = #{username}
</when >
<otherwise >
id = 1
</otherwise >
</choose >
</where >
</select >
forEach
collection 数组或集合
item 每一个数据
open 开始符号
close 结束符号
separator 分隔符
int deleteUserMore (@Param("ids") int [] ids) ;
<delete id ="deleteUserMore" >
delete from t_user where id in
<foreach collection ="ids" item ="id" open ="(" close =")" separator ="," >
#{id}
</foreach >
</delete >
int i = mapper.deleteUserMore(new int []{16 , 17 });
System.out.println(i);
DEBUG 11 -07 17 :07 :05 ,751 ==> Preparing: delete from t_user where id in ( ? , ? )(BaseJdbcLogger.java:137)
DEBUG 11 -07 17 :07 :05 ,796 ==> Parameters: 16 (Integer), 17 (Integer)(BaseJdbcLogger.java:137)
DEBUG 11 -07 17 :07 :05 ,962 <== Updates: 2 (BaseJdbcLogger.java:137)
2
int insertUserMore (@Param("users") List<User> users) ;
<insert id ="insertUserMore" >
delete from t_user where id in
<foreach collection ="users" item ="user" separator ="," >
(null,#{user.username},#{user.password},#{user.roleId},null)
</foreach >
</insert >
sql
sql片段,需要时引入
设置片段
<sql id ="getUserByIdColumns" >
id,username,roleid
</sql >
引用片段
<select id ="getUserById" resultType ="User" >
select <include refid ="getUserByIdColumns" /> from t_user where id = #{id}
</select >
MyBatis缓存
缓存只对查询有效
一级缓存
默认开启
一级缓存是SqlSession级别 的, 通过同一个SqISession查询的数据会被缓存,下次查询相同的数据,就会从缓存中
直接获取,不会从数据库重新访问
使一级缓存失效的四种情况 :
缓存效果示例
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.getUserById(2 );
System.out.println(user1);
User user2 = mapper.getUserById(2 );
System.out.println(user2);
User user3 = mapper.getUserById(2 );
System.out.println(user3);
UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession.getMapper(UserMapper.class);
User user1 = mapper1.getUserById(2 );
System.out.println(user1);
User user2 = mapper2.getUserById(2 );
System.out.println(user2);
User user3 = mapper3.getUserById(2 );
System.out.println(user3);
日志
DEBUG 11-07 17:30:58,459 ==> Preparing: select id ,username,roleid from t_user where id = ?(BaseJdbcLogger.java:137)
DEBUG 11-07 17:30:58,493 ==> Parameters: 2(Integer)(BaseJdbcLogger.java:137)
DEBUG 11-07 17:30:58,522 <== Total: 1(BaseJdbcLogger.java:137)
User{id =2, username='zhangsan2' , password='null' , roleId=1, role=null}
User{id =2, username='zhangsan2' , password='null' , roleId=1, role=null}
User{id =2, username='zhangsan2' , password='null' , roleId=1, role=null}
失效(两个不同的SqlSession不共享同一个缓存)
SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
SqlSession sqlSession2 = SqlSessionUtils.getSqlSession();
二级缓存
需要手动开启
二级缓存是SqlSessionFactory级别 , 通过同一个SqlSessionFactory创建的SqISession 查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件 :(四个条件缺一不可)
在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
在映射文件(*Mapper.xml)中设置标签<cache />
<mapper namespace ="" >
<cache />
</mapper >
二级缓存必须在SqlSession关闭或提交之后有效
sqlSession.commit();
sqlSession.close();
查询的数据所转换的实体类类型必须实现序列化的接口
public class User implements Serializable {}
使二级缓存失效的情况 :
两次查询之间执行了任意的增删改 ,会使一级和二级缓存同时失效
满足上面四个条件之后,缓存效果示例
InputStream is = Resources.getResourceAsStream("mybatis-config.xml" );
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder ();
SqlSessionFactory ssf = ssfb.build(is);
SqlSession sqlSession1 = ssf.openSession(true );
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
System.out.println(mapper1.getUserById(1 ));
sqlSession1.close();
SqlSession sqlSession2 = ssf.openSession(true );
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
System.out.println(mapper2.getUserById(1 ));
sqlSession2.close();
DEBUG 11 -07 17 :58 :14 ,621 Cache Hit Ratio [com.learn.mybatis.mapper.UserMapper]: 0.0 (LoggingCache.java:60 )
DEBUG 11 -07 17 :58 :14 ,831 ==> Preparing: select id,username,roleid from t_user where id = ?(BaseJdbcLogger.java:137 )
DEBUG 11 -07 17 :58 :14 ,875 ==> Parameters: 1 (Integer)(BaseJdbcLogger.java:137 )
DEBUG 11 -07 17 :58 :14 ,909 <== Total: 1 (BaseJdbcLogger.java:137 )
User{id=1 , username='zhangsan1' , password='null' , roleId=1 , role=null }
WARN 11 -07 17 :58 :14 ,919 As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https:
DEBUG 11 -07 17 :58 :14 ,922 Cache Hit Ratio [com.learn.mybatis.mapper.UserMapper]: 0.5 (LoggingCache.java:60 )
User{id=1 , username='zhangsan1' , password='null' , roleId=1 , role=null }
Cache Hit Ratio - 缓存命中率
二级缓存相关配置
在mapper配置文件中添加的cache标签可以设置一些属性:
eviction:缓存回收策略,默认的是LRU
LRU (Least Recently Used) - 最近最少使用 的:移除最长时间不被使用的对象。
FIFO (First in First out) - 先进先出 :按对象进入缓存的顺序来移除它们。
SOFT -软引用:移除基于垃圾回收器状态和软引|用规则的对象。
WEAK -弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval:刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(非查询/更新)时刷新
size:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly::只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性
能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一-些, 但是安全,因此默认是false。
缓存查询顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据 ,可以拿来直接使用。(范围大,更可能拿到)
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存;
整合第三方EHCache
配置
第三方只能代替二级缓存,一级缓存没有办法被代替
添加依赖
<dependency >
<groupId > org.mybatis.caches</groupId >
<artifactId > mybatis-ehcache</artifactId >
<version > 1.2.1</version >
</dependency >
<dependency >
<groupId > ch.qos.logback</groupId >
<artifactId > logback-classic</artifactId >
<version > 1.2.3</version >
</dependency >
resources/ehcache.xml
<ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation ="../config/ehcache.xsd" >
<diskStore path ="E:\ehcache" />
<defaultCache
maxElementsInMemory ="1000"
maxElementsOnDisk ="10000000"
eternal ="false"
overflowToDisk ="false"
timeToIdleSeconds ="120"
timeToLiveSeconds ="120"
diskExpiryThreadIntervalSeconds ="120"
memoryStoreEvictionPolicy ="LRU" >
</defaultCache >
</ehcache >
配置解释
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add(“demoCache”)创建Cache时,EhCache便会采用指定的的管理策略
以下属性是必须的:
maxElementsInMemory : 在内存中缓存的element的最大数目
maxElementsOnDisk:在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal :设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk:设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds: 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds : 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent: 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds :磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy :当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
使用ehcache
resources/ehcache.xml
<cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
使用slf4j日志
resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug ="false" >
<property name ="LOG_HOME" value ="E:\logback" />
<appender name ="STDOUT" class ="ch.qos.logback.core.ConsoleAppender" >
<encoder class ="ch.qos.logback.classic.encoder.PatternLayoutEncoder" >
<pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern >
</encoder >
</appender >
<appender name ="FILE" class ="ch.qos.logback.core.rolling.RollingFileAppender" >
<rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" >
<FileNamePattern > ${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern >
<MaxHistory > 30</MaxHistory >
</rollingPolicy >
<encoder class ="ch.qos.logback.classic.encoder.PatternLayoutEncoder" >
<pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern >
</encoder >
<triggeringPolicy class ="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy" >
<MaxFileSize > 10MB</MaxFileSize >
</triggeringPolicy >
</appender >
<logger name ="org.hibernate.type.descriptor.sql.BasicBinder" level ="TRACE" />
<logger name ="org.hibernate.type.descriptor.sql.BasicExtractor" level ="DEBUG" />
<logger name ="org.hibernate.SQL" level ="DEBUG" />
<logger name ="org.hibernate.engine.QueryParameters" level ="DEBUG" />
<logger name ="org.hibernate.engine.query.HQLQueryPlan" level ="DEBUG" />
<logger name ="com.apache.ibatis" level ="TRACE" />
<logger name ="java.sql.Connection" level ="DEBUG" />
<logger name ="java.sql.Statement" level ="DEBUG" />
<logger name ="java.sql.PreparedStatement" level ="DEBUG" />
<root level ="DEBUG" >
<appender-ref ref ="STDOUT" />
<appender-ref ref ="FILE" />
</root >
<logger name ="com.learn.mybatis.mapper" level ="DEBUG" />
</configuration >
mybatis-generator
根据表生成实体类
配置
pom.xml
<dependency >
<groupId > org.mybatis.generator</groupId >
<artifactId > mybatis-generator-core</artifactId >
<version > 1.4.1</version >
</dependency >
<build >
<plugins >
<plugin >
<groupId > org.mybatis.generator</groupId >
<artifactId > mybatis-generator-maven-plugin</artifactId >
<version > 1.4.1</version >
<configuration >
<verbose > true</verbose >
<overwrite > true</overwrite >
<configurationFile >
/src/main/resources/generatorConfig.xml
</configurationFile >
</configuration >
<dependencies >
<dependency >
<groupId > mysql</groupId >
<artifactId > mysql-connector-java</artifactId >
<version > 8.0.28</version >
</dependency >
<dependency >
<groupId > org.mybatis.generator</groupId >
<artifactId > mybatis-generator-core</artifactId >
<version > 1.4.1</version >
</dependency >
</dependencies >
<configuration >
<configurationFile > src/main/resources/generatorConfig.xml</configurationFile >
<verbose > true</verbose >
<overwrite > true</overwrite >
</configuration >
</plugin >
</plugins >
</build >
generatorConfig.xml
<?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 >
<context id ="DB2Table" targetRuntime ="MyBatis3" >
<jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver"
connectionURL ="jdbc:mysql://localhost:3306/xxx"
userId ="root"
password ="0101" >
</jdbcConnection >
<javaModelGenerator targetPackage ="com.learn.mybatis.model" targetProject ="src\main\java" >
<property name ="enableSubPackages" value ="true" />
<property name ="trimStrings" value ="true" />
</javaModelGenerator >
<sqlMapGenerator targetPackage ="com.learn.mybatis.mapper" targetProject ="src\main\resources" >
<property name ="enableSubPackages" value ="true" />
</sqlMapGenerator >
<javaClientGenerator type ="XMLMAPPER" targetPackage ="com.learn.mybatis.mapper" targetProject ="src\main\java" >
<property name ="enableSubPackages" value ="true" />
</javaClientGenerator >
<table tableName ="t_xxx1" domainObjectName ="xxx1" />
<table tableName ="t_xxx2" domainObjectName ="xxx2" />
</context >
</generatorConfiguration >
MyBatis使用示例
@Test
public void test02 () throws IOException {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
List<TUser> tUsers = mapper.selectByExample(null );
tUsers.forEach(System.out::println);
TUserExample example = new TUserExample ();
example.createCriteria()
.andUsernameLike("%123%" )
.andUserstatusEqualTo(1 );
List<TUser> list = mapper.selectByExample(example);
list.forEach(System.out::println);
}
TUserExample example = new TUserExample ();
example.createCriteria()
.andUsernameLike("%123%" )
.andUserstatusEqualTo(1 );
example.or()
.andUsernameLike("%aa%" )
.andUserstatusEqualTo(10 );
List<TUser> list = mapper.selectByExample(example);
list.forEach(System.out::println);
TUser user = new TUser (null ,"mybatis" ,"mybatis" ,"" ,"" ,"" ,3 );
mapper.insert(user);
user.setCreatetime("2022-11-11" );
user.setUserstatus(2 );
mapper.updateByPrimaryKeySelective(user);
...
分页插件
配置
pom.xml
<dependency >
<groupId > com.github.pagehelper</groupId >
<artifactId > pagehelper</artifactId >
<version > 5.3.0</version >
</dependency >
mybatis-config-xml
<plugins >
<plugin interceptor ="com.github.pagehelper.PageInterceptor" />
</plugins >
使用
@Test
public void test () throws IOException {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
PageHelper.startPage(1 ,3 );
mapper.selectByExample(null );
}
所执行的SQL
SELECT count (0 ) FROM t_xxx
select id, username, pwd, headImg, createTime, loginTime, userStatus from t_xxx LIMIT ?
@Test
public void test02 () throws IOException {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
PageHelper.startPage(1 ,3 );
List<TUser> list = mapper.selectByExample(null );
PageInfo<TUser> pageInfo = new PageInfo <>(list,3 );
System.out.println(pageInfo);
}
PageInfo结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术