一、使用easycode方向生成代码
通用mapper
1.加jar
<!-- 通用Mapper -->
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>mapper</artifactId>
<version>3.0.1</version>
</dependency>
2配置插件(Mapper在Mybatis的拦截器)
<plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
<!--主键自增回写方法,默认值MYSQL -->
<!--<property name="IDENTITY" value="MYSQL" />-->
<!--通用Mapper默认接口,我们定义的Mapper需要实现该接口 -->
<property name="mappers" value="com.github.abel533.mapper.Mapper" />
</plugin>
3编写接口继承Mapper
public interface IDeptDaoCo extends Mapper<Dept> {
//删除这里面他自动生成的方法
}
4配置实体类与表之间的关系
@Table(name="dept")
public class Dept implements Serializable {
@Id
private Integer deptno;
private String dname;
private String loc;
}
测试:
@Test
public void testQuery(){
Dept d=new Dept();
d.setDname("test");
Dept depts =mapper.selectByPrimaryKey(20);
System.out.println(depts);
}
@Test
public void testSelectByExample(){
Example example = new Example(Dept.class);
//.andEqualTo("deptno", 10).
example.createCriteria().andBetween("deptno", 10, 22);
example.or(example.createCriteria().andLike("dname", "李"));
example.setOrderByClause("deptno desc");
List<Dept> list = mapper.selectByExample(example);
System.out.println(list);
}
说明:
1.表名默认使用类名,驼峰转下划线(只对大写字母进行处理),如UserInfo默认对应的表名为user_info。
2.表名可以使用@Table(name = “tableName”)进行指定,对不符合第一条默认规则的可以通过这种方式指定表名.
3.字段默认和@Column一样,都会作为表字段,表字段默认为Java对象的Field名字驼峰转下划线形式.
4.可以使用@Column(name = “fieldName”)指定不符合第3条规则的字段名
5.使用@Transient注解可以忽略字段,添加该注解的字段不会作为表字段使用.
6.建议一定是有一个@Id注解作为主键的字段,可以有多个@Id注解的字段作为联合主键.
7.如果是MySQL的自增字段,加上@GeneratedValue(generator = “JDBC”)即可。如果是其他数据库,目前我并不知道怎么做
8.实体里面不建议使用基本数据类型如int类型默认是为0而且无法消除
继承通用的Mapper,必须指定泛型
一旦继承了Mapper,继承的Mapper就拥有了Mapper所有的通用方法:
Insert
方法:int insert(T record);
说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
方法:int insertSelective(T record);
说明:保存一个实体,null的属性不会保存,会使用数据库默认值
Update
方法:int updateByPrimaryKey(T record);
说明:根据主键更新实体全部字段,null值会被更新
方法:int updateByPrimaryKeySelective(T record);
说明:根据主键更新属性不为null的值
Delete
方法:int delete(T record);
说明:根据实体属性作为条件进行删除,查询条件使用等号
方法:int deleteByPrimaryKey(Object key);
说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
Example方法
方法:List selectByExample(Object example);
说明:根据Example条件进行查询
重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
方法:int selectCountByExample(Object example);
说明:根据Example条件进行查询总数
方法:int updateByExample(@Param(“record”) T record, @Param(“example”) Object example);
说明:根据Example条件更新实体record包含的全部属性,null值会被更新
方法:int updateByExampleSelective(@Param(“record”) T record, @Param(“example”) Object example);
说明:根据Example条件更新实体record包含的不是null的属性值
方法:int deleteByExample(Object example);
说明:根据Example条件删除数据
二、pageHelper分页插件使用
1.加jar
maven使用如下:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.4</version>
</dependency>
jar包:
2.xml文件配置
使用XML文件来配置Mybatis的PageHelper分页插件(放在mybatis的配置文件里面):
<!--配置开启自动匹配驼峰-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--配置PageHelper分页插件拦截器-->
<plugins>
<!--拦截器-->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
//<property name="offsetAsPageNum" value="true"/>
//<!--配置数据库方言 4.0版本以后不用写-->
//<!--<property name="helperDialect" value="mysql"/>-->
//<!--是否做count查询-->
//<property name="rowBoundsWithCount" value="true"/>
//<!--合理化分页-->
//<property name="reasonable" value="true"/>
</plugin>
</plugins>
3.测试:
@Test
public void testFindByPage(){
PageHelper.startPage(2,2);
//
List<Emp> all = mapper.getAll();
/*是否需要计算总条数*/
PageInfo<Emp> pageInfo = new PageInfo<>(all);
//返回的是Page对象,Page是ArrayList的子类。由于Page重写了toString方法
List<Emp> list = pageInfo.getList();
//SQL查询的数据总条数
System.out.println("total:数据总条数"+pageInfo.getTotal());
//总分页数
System.out.println("pages:总分页数"+pageInfo.getPages());//8
//自动生成一个分页导航,大小为8(如果满足)[1, 2, 3, 4, 5, 6, 7, 8]
System.out.println("navigatepageNums:分页导航"+ Arrays.toString(pageInfo.getNavigatepageNums()));
//分页导航的第一页
System.out.println("navigateFirstPage:分页导航的第一页"+pageInfo.getNavigateFirstPage());//1
//分页导航的最后一页
System.out.println("navigateLastPage:分页导航的最后一页"+pageInfo.getNavigateLastPage());//8
//分页导航的总页数
System.out.println("navigatePages:分页导航的总页数"+pageInfo.getNavigatePages());//8
//当前页
System.out.println("pageNum:当前页"+pageInfo.getPageNum());//2
//当前页的上一页
System.out.println("prePage:当前页的上一页"+pageInfo.getPrePage());//1
//当前页的下一页
System.out.println("nextPage:当前页的下一页"+pageInfo.getNextPage());//3
//每页的数据条数
System.out.println("pageSize:每页的数据条数"+pageInfo.getPageSize());//3
//当前页的开始行号
System.out.println("startRow:当前页的开始行号"+pageInfo.getStartRow());//4
//当前页的结束行号
System.out.println("endRow:当前页的结束行号"+pageInfo.getEndRow());//6
}
三、插件开发(拦截器):
官网地址:https://mybatis.org/mybatis-3/zh/configuration.html#plugins
Mybatis拦截器设计的初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。通过Mybatis拦截器我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。所以Mybatis拦截器的使用范围是非常广泛的。
Mybatis里面的核心对象还是比较多,如下:
Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、ParameterHandler、StatementHandler、ResultSetHandler四个对象里面的方法。
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
接口
Executor是 Mybatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射,另外,他还处理了二级缓存的操作。从这里可以看出,我们也是可以通过插件来实现自定义的二级缓存的。
StatementHandler是Mybatis直接和数据库执行sql脚本的对象。另外它也实现了Mybatis的一级缓存。这里,我们可以使用插件来实现对一级缓存的操作(禁用等等)。
ParameterHandler是Mybatis实现Sql入参设置的对象。插件可以改变我们Sql的参数默认设置。
ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口对象。我们可以定义插件对Mybatis的结果集自动映射进行修改。
分页插件
拦截器分页
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
注解详解:
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
@Intercepts标记了这是一个Interceptor
@Signature 定义拦截点
@type:需要拦截的对象,只可取四大对象之一
@method:拦截的对象方法。
@args:拦截的对象方法参数。
接口Interceptor方法详解:
//进行拦截的时候要执行的方法
Object intercept(Invocation var1) throws Throwable;
可写代码示例:
Object target = invocation.getTarget(); //被代理对象
Method method = invocation.getMethod(); //代理方法
Object[] args = invocation.getArgs(); //方法参数
// do something ...... 方法拦截前执行代码块
Object result = invocation.proceed();
// do something .......方法拦截后执行代码块
return result;
//plugin方法是拦截器用于封装目标对象的,
//返回参数target对象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理对象
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
//配置自定义属性
default void setProperties(Properties properties) {
}
实现原理:
我们知道要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,
Mybatis在执行Sql语句前也会产生一个包含Sql语句的Statement对象,
而且对应的Sql语句是在Statement之前产生的,
所以我们就可以在它成Statement之前对用来生成Statement的Sql语句下手。
在Mybatis中Statement语句是通过RoutingStatementHandler对象的prepare
方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler
接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,
之后再调用StatementHandler对象的prepare方法,即调用invocation.proceed()。
具体代码:
分页工具类:
package com.aaa.mybatis.plugin;
/**
* fileName:Page
* description:
* author:zz
* createTime:2020/9/17 18:15
* version:1.0.0
*/
public class Page {
/**
* 当前页码
*/
private Integer pageIndex;
/**
* 每页数据条数
*/
private Integer pageSize;
/**
* 总数据数
*/
private Integer total;
/**
* 总页数
*/
private Integer totalPage;
public Page() {
}
public Page(Integer pageIndex, Integer pageSize) {
this.pageIndex = pageIndex;
this.pageSize = pageSize;
}
public Integer getPageIndex() {
return pageIndex;
}
public void setPageIndex(Integer pageIndex) {
this.pageIndex = pageIndex;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getTotalPage() {
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
}
分页插件类:
package util;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.Properties;
/**
*告诉mybatis当前的插件拦截的对象是什么 以及对应的拦截的方法是什么
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class,Integer.class}),
})
public class PageInter implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//获取一下当前的代理的对象是什么
Object target = invocation.getTarget();
System.out.println("代理的对象是什么===="+target);
//获取第一个参数
Connection conn = (Connection) invocation.getArgs()[0];
//获取代理的对象的元数据
MetaObject metaObject = SystemMetaObject.forObject(target);
//
BoundSql value = (BoundSql) metaObject.getValue("delegate.boundSql");
System.out.println("绑定的sql语句===="+value.getSql());
String oldsql=value.getSql();
//开始组装新的sql语句 如果value中有参数 而且参数是Page的话就执行分页
Object parameterObject = value.getParameterObject();
if(parameterObject instanceof Page){
Page page=(Page)parameterObject;
int pageNum = page.getPageIndex();//当前的页数
int pageSize = page.getPageSize();//每页显示的条数
//开始执行分页
String countsql="select count(*) cn from ( "+oldsql+" )";
PreparedStatement statement = conn.prepareStatement(countsql);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()){
//获取总的条数
int total = resultSet.getInt("cn");
page.setTotal(total);
//一共有多少页
int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
page.setTotalPage(totalPage);
}
String newSql = oldsql + " limit ?,?";
//占位符进行赋值 JDBC
//PreparedStatement ps=Connection.
PreparedStatement ps = conn.prepareStatement(newSql);
//占位符 赋值
ps.setInt(1, (pageNum - 1) * pageSize);
ps.setInt(2, pageSize);
return ps;
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
// return null;
}
@Override
public void setProperties(Properties properties) {
System.out.println("......."+properties.getProperty("aaa"));
}
}
测试方法:
@Test
public void testPageDept(){
SqlSession sqlSession = null;
try {
// System.out.println("abc".indexOf("d"));
//使用工具类获取sqlSession
sqlSession = SqlSessionFactoryUtil.getSqlSession();
//使用jdk代理(代理接口)生成接口DeptDao代理对象
DeptDao deptDao = sqlSession.getMapper(DeptDao.class);//多态
Page page =new Page(2,3);
//获取列表
List<Dept> deptList = deptDao.pageDept(page);
System.out.println(page.getTotal()+","+page.getTotalPage());
//判断循环,输出
if(deptList!=null&&deptList.size()>0){
for (Dept dept : deptList) {
System.out.println("部门名称:"+dept.getDeptName()+",位置:"+dept.getLoc());
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(sqlSession!=null){
//官网建议放finally 关闭
sqlSession.close();
}
}
}