目录
介绍
mybatis核心配置文件:
映射文件:
mybatis执行原理
mybatis获取参数的两种方式#{}和${}(重点)
MyBatis获取Mapper方法的参数值的各种情况
MyBatis针对表的各种查询
MyBatis处理模糊查询
MyBtis处理批量删除
MyBatis根据动态的表名查询
MyBatis获取自增的主键值
数据库表字段名和实体类属性名的映射
一对一映射
多对一映射
解决方式一:使用自定义结果集映射resultMap解决上述问题(级联属性):
解决方式二:在resulMap中使用association标签定义多对一映射关系
解决方式三:使用分步查询
编辑
延迟加载
一对多映射
使用分步查询
用一条sql语句完成查询
MyBatis实现动态SQL
if标签
where标签
trim标签
choose、when、otherwise一套标签
foreach循环遍历
foreach实现批量删除
foreach实现批量添加
SQL标签
MyBatis缓存
MyBatis的一级缓存
MyBatis的二级缓存
MyBatis缓存查询的顺序
整合第三方缓存EHCache(了解)
MyBatis的逆向工程
创建逆向工程的步骤
奢华尊享版的逆向工程功能测试
MyBatis使用分页插件
分页插件使用步骤
最近学习了MyBaits框架,来记录一下我的学习体会。
首先MyBatis是持久层的一个框架,他封装了JDBC,几乎避免了手写JDBC的绝大部分代码,可以避免那些重复而繁多的JDBC代码。
MyBatis框架=mybatis核心jar包+配置文件,两个结合一起工作。
配置文件分为 核心配置文件 和 映射文件。
核心配置文件(mybatis-config.xml):配置连接数据库的信息以及MyBatis的全局配置信息
映射文件(XXXMaper.xml):主要写sql语句,一个操作数据库表的Mapper接口,就对应一个映射文件。
置文当我们调用Mapper接口中的方法时,Mybatis会找到核心配置文件,读取映射文件,然后找到当前Mapper接口对应的映射文件,然后根据方法名在映射文件中定位到唯一的sql语句进行执行。
1.我们在Mapper接口中定义好操作数据库表的方法,如:getUserByUserName(String username)
/**
*
* 操作数据库中User表的Mapper,相当于之前我们学的Dao接口。
* 现在不用写实现类,MyBatis底层会使用代理模式创建Mapper接口的实现类对象然后返回,
* 我们直接使用这个实现类对象调用接口中的方法即可,他会自动去找映射文件中对应的sql语句执行。
*
* @author shkstart
* @create 2022-05-25 16:26
*/
public interface UserMapper {
/**
* 根据用户名查找用户信息
* @return
*/
User getUserByUserName(String username);
}
2.Mapper对应的映射文件UserMapper.xml
3.调用Mapper接口中的方法
@Test
public void testGetUserByUserName() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
UserMapper userMapper1 = new SqlSessionFactoryBuilder().build(is)
.openSession(true).getMapper(UserMapper.class);
// "张三"就是sql语句的参数
User user = userMapper.getUserByUserName("张三");
System.out.println(user);
}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
- #{} 对应的变量会自动加上单引号 ,${} 对应的变量不会加上单引号
- 字符串类型数据、日期类型数据、字段名等可以加单引号
我们底层的东西就先不深挖了,先学会用。
调用了Mapper接口中的 User getUserByUserName(String username); 方法后:
1、MyBatis会根据这个Mapper的全类名com.lin.mybatis.mapper.UserMapper找到它所对应的映射文件UserMapper.xml,
2、然后根据方法名getUserByUserName在映射文件中找到对应id为getUserByUserName的sql语句:
select * from t_user where name = #{username}
select * from t_user where name = ${username}
3、接着就是MyBatis会预编译上述找到的sql语句:
a> "select * from t_user where name = ?" (体现占位符)
b> "select * from t_user where name =" + ${username} + " (体现字符串拼接)
4、接着应该是MyBatis获取参数(这个参数就是调用Mapper中的方法的实参)填充占位符了
a>select * from t_user where name = '张三'
b>select * from t_user where name = '张三'
5、最后执行sql语句返回结果集
1.当Mapper方法中的参数为一个时,此时可以使用 ${}和#{} 以任意的名称(最好见名识意)获取参数的值,注意${}需要手动加单引号 可以用@param代替 2.当Mapper接口中的方法的参数为多个时,调用方法时,MyBatis会将参数值放到一个Map集合中,可以通过 ${键}和#{键} 的方式来获取参数值。 键有两种形式: a:arg0,arg1,..... b:param1,param2,... 值(就是用户传过来的参数值): 按照键的顺序一一对应: param1对应参数值1,param2对应参数值2,...... 3.当Mapper接口方法的参数有多个时,可以将方法的参数设置为一个Map集合,调用方法时,这样就和第2种情况一样 可以通过 ${键}和#{键} 的方式来访问值。 select * from t_user where name = #{username} and password = #{password} 4.当Mapper接口方法的参数是一个实体类类型时,调用此方法时, 映射文件中的sql语句可以通过可以通过 ${属性值}和#{属性名} 的方式来访问属性值。 5.使用@Param注解来命名注解 MyBatis会将@Param注解的值当成Map集合的key,参数值当成Map集合的value 也会把param1,param2,....当成键 两种.可以通过 ${键}和#{键} 的方式来获取参数值。总结:可以只分为下面两种情况来处理 实体类型参数和 @param情况.
代码理解:
@Test
public void testGetUserByNameAndPwd(){
UserMapper userMapper = SqlSessionUtil.getSqlSession().getMapper(UserMapper.class);
User user = userMapper.getUserByNameAndPwd("root","ni");
System.out.println(user);
}
@Test
public void testInsertUser(){
UserMapper userMapper = SqlSessionUtil.getSqlSession().getMapper(UserMapper.class);
User user = new User(23, "梨花", "lll", "女", 12);
int count = userMapper.insertUser(user);
System.out.println(count);
}
public interface UserMapper {
/**
* 根据用户名和密码查询用户信息
key value
uName root
password ni
* @return
*/
User getUserByNameAndPwd(@Param("uName") String username,@Param("password") String pwd);
/**
* 增加用户
key value
id 23
name 梨花
password 111
sex 女
age 12
* @param user
* @return
*/
int insertUser(User user);
}
insert into t_user values(null,#{name},#{password},#{sex},#{age})
* 针对表的各种查询 * 1.若查询出来的结果集有多条记录: * a: 可以通过List集合来接收(List集合中每个元素是一个实体类类型) * b: 可以通过List集合来接收(List集合中每个元素是一个Map类型,Map中是每条记录的键值对) * [{password=ni, sex=男, name=root, id=2, age=12}, {password=root, sex=男, name=dfd, id=3, age=12}] * c: 使用@MapKey注解, @MapKey("id")以查询的记录中的某个唯一的字段为键,每条记录构成的Map为值。 * {2={password=ni, sex=男, name=root, id=2, age=12}, 3={password=root, sex=男, name=dfd, id=3, age=12}} * * 注意:不能用实体类对象来接收,会报TooManyResultsException异常(多对一) * * 2.若查询出来的结果集只有一条记录 * a: 可以通过实体类对象来接收 * b: 也可以通过List集合来接收 * c: 也可以通过Map集合来接收(字段名为键,字段值为值)
代码理解:
public interface SelectMapper {
/**
* 查询所有的用户信息,并将所有的用户封装进一个集合中(多条记录)
* a: 可以通过List集合来接收(List集合中每个元素是一个实体类类型)
* @return
*/
// User getAllUser();错的,报TooManyResultsException异常
List getAllUser();
/**
* 查询所有的用户信息,(多条记录)
* b:可以通过List集合来接收(List集合中每个元素是一个Map类型,每个Map中是每条记录的键值对)
* @return
*/
List
public interface LikeQueryMapper {
/**
* 使用模糊的用户名查询用户信息
*
* @param likeName
* @return
*/
List getUserByLikeName(@Param("likeName")String likeName);
}
其中select * from t_user where name like "%"#{likeName}"%" 最常用
/**
* 根据用户的ids批量删除多个用户
*
* @param ids
* @return
*/
int deleteUserByIds(@Param("ids")String ids);
delete from t_user where id in(#{ids})
@Test
public void deleteUserByIdsTest(){
SQLMapper mapper = SqlSessionUtil.getSqlSession().getMapper(SQLMapper.class);
int count = mapper.deleteUserByIds("5,6,7");
System.out.println(count);
}
运行截图:
从以上现象可以看出,实现批量删除不能使用#{}的方式获取参数值,因为它会自动将参数值加上单引号,即会将delete from t_user where id in(#{ids}) 解析成delete from t_user where id in('5,6,7'); 只有id为5,6,7的数据会被删除
而批量删除正确的sql语句应该是:delete from t_user where id in(5,6,7);
所以批量删除只能用${}的方式获取参数值,它不会自动加单引号。
delete from t_user where id in(${ids})
执行结果:
/**
* 自定义要查询的表,根据传来的表名进行查询
*
* @param tableName
* @return
*/
List getUserByTable(String tableName);
@Test
public void getUserByTableTest(){
SQLMapper mapper = SqlSessionUtil.getSqlSession().getMapper(SQLMapper.class);
List users = mapper.getUserByTable("t_user");
System.out.println(users);
}
运行结果:
可以看出来使用#{}的方式出错了,因为他会给要查询的表名加上单引号,
select * from 't_user';
而我们的表名是不能加单引号,所以这个也只能用${}的方式获取参数值。
这个是干嘛的?
业务场景:
有一个班级信息表t_class(class_id,class_name);
有一个学生信息表t_student(student_id,stu_name,class_id);
1、要添加一个班级信息
2、获取新添加班级的主键值(MyBatis获取自增的主键值)
3、然后添加一个学生信息,同时指定学生所在的班级为刚刚添加的班级。(所以就需要获取刚刚添加班级的主键class_id)
使用:
/**
* 插入用户
*
* @param user
* @return
*/
int insertUser(User user);
insert into t_user values(null,#{username},#{password},#{sex},#{age})
@Test
public void insertUserTest(){
SQLMapper mapper = SqlSessionUtil.getSqlSession().getMapper(SQLMapper.class);
User user = new User(null, "测试名", "234", "男", 23);
// 插入用户信息,id为空,但是表里面id字段是自增的,会将自增的id赋给user
int count = mapper.insertUser(user);
System.out.println(user);
}
运行结果:
表:
对应实体类:
以下的查询sql,执行完毕后,会根据结果集中的字段名调对应的名字的属性名然后调该属性的set方法,将字段对应的值设置给属性。
问题引出,但是由于属性和字段的命名规则不一样,所以可能会出现以下情况:
问题解决,倘若字段名和属性名不一致,可以通过以下两种方式处理字段和属性之间的映射关系:
1、给查询的字段名其别名,这个别名要和属性名一样
2、在MyBatis核心配置文件中定义全局设置,
3、使用resultMap自定义字段名和属性名的映射关系
多个员工对应一个部门
业务背景:
想要查询员工的信息,
// 员工表t_emp对应的实体类
public class Emp {
private Integer eid;
private String empName;
private String sex;
private Integer age;
private String email;
private Dept dept;// 员工所在的部门
}
/**
* 根据员工编号,查询所有的员工信息与其所在的部门信息
*
* @return
*/
Emp getEmpAndDeptById(@Param("eid")Integer eid);
查询结果集:
运行结果:
结果集的字段和实体类的属性映射失败,导致某些属性没有赋值。
MyBatis会根据自定义结果集映射将查询出来的结果集按照此套映射,将字段值设置给对应属性,比如:emp_name查询出来的值为 周杰伦,对应Emp的empName属性设置为 周杰伦;dept_name为B,对应Emp的dept的deptName设置为B。
@Test
public void getEmpAndDeptByIdTest(){
EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptById(2);
System.out.println(emp);
}
调用Mapper接口中的方法,会执行getEmpAndDeptById这个sql语句,然后根据empAndDeptResultMap2映射关系,处理结果集和实体类型Emp之间的对应关系。
某个字段使用其他的查询结果赋值。
/**
* 根据员工id查询员工信息
*
* @param eid
* @return
*/
Emp getEmpById(Integer eid);
查询部门:
延迟加载:只根据所需的数据执行对应的sql语句,不会执行不相关的sql语句。倘若没有开启延迟加载的话,某些sql语句之间产生关联了,然后所需的数据只需要执行其中的一条sql语句即可,但是由于没有开启延迟加载,就得把这些关联的sql语句全部都执行,然后获取所需的数据。
分步查询的优点:可以实现延迟加载,但是必须在MyBatis核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。(默认为false)
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载。(默认为false)
以下就是分步查询的配置,查询员工信息dept,员工所对应的部门信息需要由另一条查询语句来查询部门信息。
要员工的全部信息(包括部门信息):
@Test
public void getEmpByIdTest(){
EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);
Emp emp = mapper.getEmpById(4);
System.out.println(emp);
}
查询员工的全部信息,所以需要查询员工对应的部门信息,所以将关联的查询部门信息的sql语句一起执行了。
只要员工的姓名信息:
@Test
public void getEmpByIdTest(){
EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);
Emp emp = mapper.getEmpById(4);
System.out.println(emp.getEmpName());
}
从运行结果可以看出,只执行了查询员工信息的sql语句select * from t_emp where eid = ?
因为我们只需要员工的名字,所以不需要查询部门信息,因此延迟加载了互相关联的查询部门信息的sql语句。
只要员工的所在的部门信息
@Test
public void getEmpByIdTest(){
EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);
Emp emp = mapper.getEmpById(4);
System.out.println(emp.getEmpName());
System.out.println("-----------------");
System.out.println(emp.getDept());
}
fetchType:指定当前的查询是否使用延迟加载。(eager表示使用,lazy表示使用)
@Test
public void getEmpByIdTest(){
EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);
Emp emp = mapper.getEmpById(4);
System.out.println(emp.getEmpName());
/* System.out.println("-----------------");
System.out.println(emp.getDept());*/
}
可以看出没有延迟加载。
一个部门有多个员工。
public interface DeptMapper {
/**
* 根据部门id查询部门信息
*
* @param did
* @return
*/
Dept getDeptById(Integer did);
}
emps属性需要由以下的sql语句来查询并赋值:
在我们的平时业务中,常常要用到多条件查询。
比如查询一个员工的信息,要根据用户填写的信息来查询,要根据员工的姓名、性别、年龄、邮箱等信息来查询员工信息。
public interface DynamicSqlMapper {
/**
* 根据员工的姓名、性别、年龄、邮箱等信息来查询员工信息。
*
* 将员工的姓名、性别、年龄、邮箱等信息封装成一个员工对象作为参数
*
* @param emp
* @return
*/
List getEmpByConditions(Emp emp);
}
@Test
public void getEmpByConditionsTest(){
DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);
// 用户可能在前端输入了要查询的员工信息
// 然后后端,获取请求参数,获取要查询的员工信息
String empName = "易烊千玺";
int age = 21;
String sex = "男";
Emp emp = new Emp(null, empName, sex, age, null);
List empList = mapper.getEmpByConditions(emp);
System.out.println(empList);
}
也有可能只输入了性别和年龄:
@Test
public void getEmpByConditionsTest(){
DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);
// 用户可能在前端输入了要查询的员工信息
// 然后后端,获取请求参数,获取要查询的员工信息
// 也有可能只输入了年龄和性别
Emp emp2 = new Emp(null, null, "男", 21, null);
List empList = mapper.getEmpByConditions(emp2);
System.out.println(empList);
}
修改映射文件:
上面的if标签,我们可以看到where关键字是写死的,那么如果下面的if标签都不成立,那么where关键字就很多余。
基于以上问题引出where标签:
1、where标签一般和if标签一起使用
2、如果where标签中有内容(就是if标签成立)则会自动生成WHERE关键字和上面的sql语句拼接,而且会将最前面的and或or关键字去除掉。
3、如果where标签中没有内容(if标签都不成立),则不会生成WHERE关键字,体现动态性。
trim用于去掉或添加标签中的内容
常用属性
prefix:在trim标签中的内容的前面添加某些内容
- suffix:在trim标签中的内容的后面添加某些内容
- prefixOverrides:在trim标签中的内容的前面去掉某些内容
- suffixOverrides:在trim标签中的内容的后面去掉某些内容
若trim中的标签都不满足条件,则trim标签没有任何效果,也就是只剩下
select * from t_emp
choose、when、otherwise
相当于if...else if..else或者switch case default
1.when至少要有一个,otherwise至多只有一个
2.when只会执行一个,如果有多个when都成立,那么谁在前面执行谁的
3.when都不成立的话,则执行otherwise中的
@Test
public void getEmpByConditionsTest(){
DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);
// 用户可能在前端输入了要查询的员工信息
// 然后后端,获取请求参数,获取要查询的员工信息
// 也有可能只输入了年龄和性别
Emp emp2 = new Emp(null, null, "男", 21, null);
List empList = mapper.getEmpByConditions(emp2);
System.out.println(empList);
}
foreach循环遍历输出,主要帮我们实现一些批量操作。
- collection:要遍历的集合或数组
- item:给集合或数组中的每一项元素起个变量名,代表集合或数组中的每一项元素
- separator:指明遍历输出的每一项数据之间的分隔符
- open:指明以什么开始遍历输出,设置foreach标签中的内容的开始符
- close:指明以什么结束遍历输出,设置foreach标签中的内容的结束符
/**
* 根据员工编号id构成的数组来删除员工
*
* @Param("eids") 到时候,mybatis会以@param注释的值为键,然后参数数组为值.
*
* @param eids
* @return
*/
int deleteEmpByEids(@Param("eids") Integer[] eids);
delete from t_emp where eid in
(
#{eid}
)
delete from t_emp where eid in
#{eid}
@Test
public void deleteEmpByEidsTest(){
DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);
int count = mapper.deleteEmpByEids(new Integer[]{6, 7, 8});
System.out.println(count);
}
#{eid} 这一整个foreach标签,最后遍历输出的就是:(6,7,8)
/**
* 批量添加员工
*@Param("empList") 到时候, mybatis会会将empList集合添加到Map集合中,
* 以@param注释的值为键,然后参数数组为值.
*
* @param empList
* @return
*/
int addEmpsByList(@Param("empList") List empList);
insert into t_emp values
(null,#{emp.empName},#{emp.sex},#{emp.age},#{emp.email},null)
@Test
public void addEmpsByListTest(){
DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);
// 要添加的员工集合
List empList = new ArrayList<>();
empList.add(new Emp(null,"a","男",12,"[email protected]"));
empList.add(new Emp(null,"b","男",23,"[email protected]"));
empList.add(new Emp(null,"c","男",25,"[email protected]"));
int count = mapper.addEmpsByList(empList);
System.out.println(count);
}
sql片段,可以记录保存一段公共sql片段,在使用的地方通过include标签进行引入
sql片段:emp_name,sex,age,email 引用:
emp_name,sex,age,email
- 缓存只针对我们的查询功能
- MyBatis会将查询的数据缓存起来,如果下次查询的数据有在缓存中,则直接从缓存中获取,没有的话才去数据库查询,减少了JDBC的操作。
一级缓存是默认开启的。
一级缓存是SqlSession级别的,通过一个SqlSession查询的数据会被缓存,下次在同一个SqlSession中查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
一级缓存的范围就是要保证在同一个SqlSession中才行。
使一级缓存失效的四种情况:
1. 不同的SqlSession对应不同的一级缓存
2. 同一个SqlSession但是查询条件不同
3. 同一个SqlSession两次查询期间执行了任何一次增删改操作,不管有没有影响到查询的数据,任何一次增删改操作MyBatis都会去清空一级缓存。
4. 同一个SqlSession两次查询期间手动清空了缓存,代码:SqlSession.clearCache();
public interface CacheMapper {
/**
*
* @param eid
* @return
*/
Emp getEmpByEid(Integer eid);
}
同一个sqlSession:
@Test
public void testCache1(){
// 一个sqlSession
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
CacheMapper mapper2 = sqlSession.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEid(1);
System.out.println(emp2);
}
不同一个sqlSession:
@Test
public void testCache2(){
// 两个sqlSession
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEid(1);
System.out.println(emp2);
}
二级缓存是SqlSessionFactory(可以理解为一个数据库连接池)级别,通过一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;后面再通过同一个SqlSessionFactory创建的SqlSession进行查询,会从二级缓存中找,如果有相同的查询,则直接从缓存中取,没有则访问数据库;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件
1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
2. 在映射文件中设置标签
3. 二级缓存必须在SqlSession关闭或提交之后有效,只有Sqlsession关闭或提交之后,查询的数据才会被保存到二级缓存中,否则保存到一级缓存中。
4. 查询的数据所转换的实体类类型必须实现序列化的接口。比如查询员工共信息,那么员工类就必须implement Serializable只有满足以上四个条件,二级缓存才能生效。
二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性
eviction属性:缓存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除他们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU
flushInterval属性:刷新间隔,就是清空缓存的时间间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新清空缓存。
size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。速度快。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
由于我们的MyBatis是一个持久层框架,所以它在做缓存功能可能没有那么专业,所以我们可以用第三方的缓存来替换MyBatis的二级缓存,但是一级缓存是替换不了的。
第一步:添加依赖
org.mybatis.caches
mybatis-ehcache
1.2.1
ch.qos.logback
logback-classic
1.2.3
各个jar包的功能:
jar包名称 | 作用 |
---|---|
mybatis-ehcache | Mybatis和EHCache的整合包 |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日志门面包 |
logback-classic | 支持SLF4J门面接口的一个具体实现 |
第二步:创建EHCache的配置文件ehcache.xml
名字必须叫`ehcache.xml`
第三步:设置二级缓存的类型
在xxxMapper.xml文件中设置二级缓存类型
第四步:加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。创建logback的配置文件logback.xml
,名字固定,不可改变
[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n
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(先进先出 |
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
逆向工程:先创建数据库表,由框架负责根据数据库中的表,反向生成如下资源:
Java实体类
Mapper接口
Mapper映射文件
第一步:在项目下创建一个Maven模块
第二步:在Maven模块的pom.xml文件中添加依赖和插件
jar
org.mybatis
mybatis
3.5.9
junit
junit
4.13.2
test
mysql
mysql-connector-java
8.0.27
log4j
log4j
1.2.17
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.0
org.mybatis.generator
mybatis-generator-core
1.3.2
com.mchange
c3p0
0.9.2
mysql
mysql-connector-java
8.0.27
第三步:创建MyBatis的核心配置文件
第四步:创建逆向工程所需的配置文件
文件名必须是:generatorConfig.xml
第五步:执行插件的generate目标进行逆向工程生成
看到以下说明成功了:
如果出现报错:Exception getting JDBC Driver
,可能是pom.xml中,数据库驱动配置错误
那你就要检查pom.xml文件中的dependencies标签中的mysql驱动要和mybatis-generator-maven-plugin插件中的mysql驱动的版本是否一致
selectByExample
:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据
example.createCriteria().andXXX
:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
example.or().andXXX
:将之前添加的条件通过or拼接其他条件
测试查询:
// 测试查询功能
@Test
public void testMBGOfQuery1() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// 查询所有的员工信息
List empList = mapper.selectByExample(null);// 传入null表示条件为null即没有条件,就是查询所有的员工
empList.forEach(emp -> System.out.println(emp));
}
代码:
// 测试查询功能
@Test
public void testMBGOfQuery2() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// 根据条件查询员工信息
EmpExample example = new EmpExample();// 用于条件查询
// 创建要查询的条件
// 1.这是添加与and条件
example.createCriteria().andEmpNameEqualTo("易烊千玺").andAgeGreaterThanOrEqualTo(10);
// 2.这是添加或or条件
example.or().andEmpNameEqualTo("周杰伦");
List empList = mapper.selectByExample(example);
for (Emp emp : empList) {
System.out.println(emp);
}
}
测试修改:
普通修改:
// 测试修改功能
@Test
public void testMBGOfUpdate1() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// updateByPrimaryKey()方法是普通的根据主键修改记录信息,如果其中有空的字段的话,还是会修改成null
int count = mapper.updateByPrimaryKey(new Emp(1, "周杰伦", "男", null, "[email protected]", 5));
System.out.println(count);
}
选择性修改:
// 测试修改功能
@Test
public void testMBGOfUpdate2() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// updateByPrimaryKeySelective()方法是选择性的根据主键修改记录信息,如果其中有空的字段的话,则这个字段会被去除掉,不会修改为null
int count = mapper.updateByPrimaryKeySelective(new Emp(1, "周杰伦", "男", null, "[email protected]", 5));
System.out.println(count);
}
第一步:在pom.xml文件中添加依赖
com.github.pagehelper
pagehelper
5.2.0
第二步:在mybatis核心配置文件中配置分页插件
第三步:在查询之前开启分页功能
开启分页功能:PageHelper.startPage(int pageNum, int pageSize);pageNum是页码,pageSize是每页显示的记录数。
// 测试分页插件功能
@Test
public void testPage1() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// 开启分页功能
PageHelper.startPage(1,4);
// 查询所有的员工信息
List empList = mapper.selectByExample(null);// 传入null表示条件为null即没有条件,就是查询所有的员工
empList.forEach(emp -> System.out.println(emp));
}
第四步:在查询完分页所需的数据之后,获取和分页相关的信息
在查询分页所需的数据,获取list集合之后,使用
PageInfo
获取分页相关数据pageInfo = new PageInfo<>(List list, intnavigatePages) PageInfo对象包含了做分页功能相关的很多信息。
list:分页的数据
navigatePages:导航分页的页码数,就是分页导航条一次显示多少个页码
// 测试分页插件功能
@Test
public void testPage1() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
// 开启分页功能
PageHelper.startPage(3, 4);
// 查询所有的员工信息,这里会获取第三页然后每页4条的数据
List empList = mapper.selectByExample(null);// 传入null表示条件为null即没有条件,就是查询所有的员工
empList.forEach(emp -> System.out.println(emp));
// 在查询完分页所需的数据后,获取和分页相关的信息(PageInfo包含了和分页功能相关的所有数据)
PageInfo page = new PageInfo<>(empList,5);
System.out.println("-------page相关---------");
System.out.println(page);
}
pageInfo:
常用数据:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
PageInfo{pageNum=3, pageSize=4, size=4, startRow=9, endRow=12, total=23, pages=6,
list=Page{count=true, pageNum=3, pageSize=4, startRow=8, endRow=12, total=23, pages=6, reasonable=false, pageSizeZero=false}[Emp{eid=12, empName='b', sex='男', age=23, email='[email protected]', did=null}, Emp{eid=13, empName='c', sex='男', age=25, email='[email protected]', did=null}, Emp{eid=14, empName='aa', sex='null', age=null, email='null', did=null}, Emp{eid=15, empName='aa', sex='null', age=null, email='null', did=null}],
prePage=2, nextPage=4, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=5, navigatepageNums=[1, 2, 3, 4, 5]}
mybatis提供了很多种日志功能:
mybatis中具体使用哪个日志,在mybatis的核心配置文件中设定。
依赖:
#log4j.rootLogger = level, appenderName1, appenderName2, …
#appenderName:就是指定日志信息要输出到哪里。可以同时指定多个输出目的地,用逗号隔开。console是输出到控制台
#level:DEBUG < INFO < WARN < ERROR < FATAL,只会输出大于等于该级别的日志信息
log4j.rootLogger=debug,console
#log4j.appender.appenderName1=className. console上面设置日志输出到哪,org.apache.log4j.ConsoleAppender表示控制台、org.apache.log4j.FileAppender表示文件
log4j.appender.console=org.apache.log4j.ConsoleAppender
#设置日志输出的格式。org.apache.log4j.PatternLayout表示可定制化、org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#ConversionPattern设置以怎样的格式输出日志信息。百度很多。
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %p %C{1} : %m%n
#log4j.logger.全类名=level 自定义指定某个类中的日志输出级别
log4j.logger.org.apache.zookeeper=WARN
log4j.logger.org.apache.dubbo.config.bootstrap.DubboBootstrap=WARN
log4j.logger.com.lin.module1.Module1Application=INFO