mybatis在springboot的集成
集成springboot很简单,直接用 boot的starter即可,
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
配置加上:
# Mysql 注意替换相应配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/aaaa?useUnicode=true&characterEncoding=utf8
spring.datasource.username=aaaa
spring.datasource.password=aaaa
如果不是用springboot,则自己网上找下教程吧.
mybatis的自动生成xml
mybatis的sql都要自己写,比较麻烦,我们使用自动生成的方式
官网:http://www.mybatis.org/generator/index.html
使用步骤:
1,在pom.xml中增加插件配置
<!-- mybatis generator 自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<configurationFile>src/main/resources/mapper/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
</plugin>
2,把要相应的配置文件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>
<!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
<classPathEntry location="src/main/resources/mapper/mysql-connector-java-5.1.46.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库链接URL,用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://xxxx:3306/aaa" userId="aaa" password="aaa">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成模型的包名和位置-->
<javaModelGenerator targetPackage="com.saaa.bsss.test.model.po.db" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成映射文件的包名和位置-->
<sqlMapGenerator targetPackage="generator" targetProject="src/main/resources/mapper">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成DAO的包名和位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.saaa.bsss.test.dao.mapper.generator" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
<table tableName="t_aaa" domainObjectName="AaaPO"></table>
<table tableName="t_bbb" domainObjectName="BaaPO"></table>
</context>
</generatorConfiguration>
注意要把 mysql-connector-java-5.1.46.jar加到工程中.同时上面所涉及到文件夹要存在。
3,执行mvn命令,生成相应的代码:
mybatis-generator:generate -e
MyBatis Generator是一个maven插件,其运行时并不依赖 spring或 mybatis,只是一个反向生成代码的工具,根据 db中的表进行映射和生成.
生成的代码运行例子
使用时,直接用生成的代码来处理,如
@Autowired
private UserPOExtMapper userDTOMapper;
public int insert(UserPO userDTO) {
userDTO.setStatus(ProjectConstant.UserConstant.USER_STATUS_OK);
userDTO.setCreateTime(new Date());
return userDTOMapper.insert(userDTO);
}
public UserPO findByUserid(Integer userid) {
UserPOExample example = new UserPOExample();
example.or().andUseridEqualTo(userid).andStatusEqualTo(ProjectConstant.UserConstant.USER_STATUS_OK);
List<UserPO> userDTOS= userDTOMapper.selectByExample(example);
if (userDTOS == null || userDTOS.size() < 1) {
return null;
}
return userDTOS.get(0);
}
mybatis的分页
分页,我们采用com.github.pagehelper
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
在使用时,
public Page<AaaPO> findByUser(int userid,int pageNum, int pageSize) {
Page<AaaPO> cpage = PageHelper.startPage(pageNum, pageSize);
AaaPOExample example = new AaaPOExample();
example.or();
List<String> tostrList = new ArrayList<>();
tostrList.add("all");
tostrList.add(userid+"");
example.or().andMToIn(tostrList);
example.setOrderByClause("update_time desc");
AaaPOMapper.selectByExample(example);
return cpage;
}
说明, 如果我们看mapper.xml,会发现,里面是没有 数据库相应的 分页逻辑, 如mysql的limit, 但我们采用 com.github.pagehelper 却可以分页,这个很诡异的事情,同时com.github.pagehelper是用静态方法来做的,高并发时不会出错吗?后续这方面的相应源码.
com.github.pagehelper是一个开源的库,是一个mybatis的插件,主要作用就是在mybatis查询的时候,针对各数据库加上分页逻辑,同时返回。
mybatis 中pageHelper的源码
我们在
com.github.pagehelper.Page中的setTotal(long total)中打个断点,
stack如下:
setTotal:189, Page (com.github.pagehelper)
afterCount:89, AbstractHelperDialect (com.github.pagehelper.dialect)
afterCount:82, PageHelper (com.github.pagehelper)
intercept:117, PageInterceptor (com.github.pagehelper)
invoke:61, Plugin (org.apache.ibatis.plugin)
query:-1, $Proxy170 (com.sun.proxy)
selectList:148, DefaultSqlSession (org.apache.ibatis.session.defaults)
selectList:141, DefaultSqlSession (org.apache.ibatis.session.defaults)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:433, SqlSessionTemplate$SqlSessionInterceptor (org.mybatis.spring)
selectList:-1, $Proxy99 (com.sun.proxy)
selectList:230, SqlSessionTemplate (org.mybatis.spring)
executeForMany:139, MapperMethod (org.apache.ibatis.binding)
execute:76, MapperMethod (org.apache.ibatis.binding)
invoke:59, MapperProxy (org.apache.ibatis.binding)
selectByExample:-1, $Proxy135 (com.sun.proxy)
findByUseridAndAll:39, MyUserDAO (com.saaa.bsss.test.dao.db)
esTest:31, MyUserDAOTest (com.saaa.bsss.test.dao.db)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
runReflectiveCall:50, FrameworkMethod$1 (org.junit.runners.model)
run:12, ReflectiveCallable (org.junit.internal.runners.model)
invokeExplosively:47, FrameworkMethod (org.junit.runners.model)
evaluate:17, InvokeMethod (org.junit.internal.runners.statements)
evaluate:73, RunBeforeTestExecutionCallbacks (org.springframework.test.context.junit4.statements)
evaluate:83, RunAfterTestExecutionCallbacks (org.springframework.test.context.junit4.statements)
evaluate:75, RunBeforeTestMethodCallbacks (org.springframework.test.context.junit4.statements)
evaluate:86, RunAfterTestMethodCallbacks (org.springframework.test.context.junit4.statements)
evaluate:84, SpringRepeat (org.springframework.test.context.junit4.statements)
runLeaf:325, ParentRunner (org.junit.runners)
runChild:251, SpringJUnit4ClassRunner (org.springframework.test.context.junit4)
runChild:97, SpringJUnit4ClassRunner (org.springframework.test.context.junit4)
run:290, ParentRunner$3 (org.junit.runners)
schedule:71, ParentRunner$1 (org.junit.runners)
runChildren:288, ParentRunner (org.junit.runners)
access$000:58, ParentRunner (org.junit.runners)
evaluate:268, ParentRunner$2 (org.junit.runners)
evaluate:61, RunBeforeTestClassCallbacks (org.springframework.test.context.junit4.statements)
evaluate:70, RunAfterTestClassCallbacks (org.springframework.test.context.junit4.statements)
run:363, ParentRunner (org.junit.runners)
run:190, SpringJUnit4ClassRunner (org.springframework.test.context.junit4)
run:137, JUnitCore (org.junit.runner)
startRunnerWithArgs:68, JUnit4IdeaTestRunner (com.intellij.junit4)
startRunnerWithArgs:47, IdeaTestRunner$Repeater (com.intellij.rt.execution.junit)
prepareStreamsAndStart:242, JUnitStarter (com.intellij.rt.execution.junit)
main:70, JUnitStarter (com.intellij.rt.execution.junit)
会被调用的机制是什么
说明pageHelper是使用 mybatis的拦截器进行操作
上面的 invoke:61, Plugin (org.apache.ibatis.plugin)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
其中上面的 interceptor 对象,就是page的拦截器!!
那拦截器是哪里被配置到mybatis的呢?
我们把pageHelper加到服务上,并没有增加显式的配置,说明有可能是用spring-boot的机制 来进行配置,
我们找到
pagehelper-spring-boot-autoconfigure-1.2.5.jar
中的 com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration 类
其中
@PostConstruct
public void addPageInterceptor() {
PageInterceptor interceptor = new PageInterceptor();
Properties properties = new Properties();
properties.putAll(this.pageHelperProperties());
properties.putAll(this.properties.getProperties());
interceptor.setProperties(properties);
Iterator var3 = this.sqlSessionFactoryList.iterator();
while(var3.hasNext()) {
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var3.next();
sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
}
}
上面的代码的 SqlSessionFactory 是org.apache.ibatis.session.SqlSessionFactory ,这个方法把interceptor初始化后扔到sqlSessionFactory中
@PostConstruct,这个annotation可以理解为,在这个方法在此类调用了construct后执行.
拦截器里面的逻辑?为什么用静态可以?
按图索骥,我们找到相应的拦截器
com.github.pagehelper.PageInterceptor
在Page cpage = PageHelper.startPage(pageNum, pageSize);时
往里走
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
...
LOCAL_PAGE.set(page);
这里说明这个page对象 是和线程绑定的,在代码运行的过程中,相应数据找到这个静态,通过线程找到对象 ,进行回填。
数据是怎么扔到page的?
逻辑上都是在 PageInterceptor中处理的,
回填是调用AbstractHelperDialect类的下面两个方法:
@Override
public boolean afterCount(long count, Object parameterObject, RowBounds rowBounds) {
Page page = getLocalPage();
page.setTotal(count);
if (rowBounds instanceof PageRowBounds) {
((PageRowBounds) rowBounds).setTotal(count);
}
//pageSize < 0 的时候,不执行分页查询
//pageSize = 0 的时候,还需要执行后续查询,但是不会分页
if (page.getPageSize() < 0) {
return false;
}
return count > 0;
}
@Override
public Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {
Page page = getLocalPage();
if (page == null) {
return pageList;
}
page.addAll(pageList);
if (!page.isCount()) {
page.setTotal(-1);
} else if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
page.setTotal(pageList.size());
} else if(page.isOrderByOnly()){
page.setTotal(pageList.size());
}
return page;
}
mybatis分页对页数超出范围的处理
分页时,如果总11条数据,5条一页,则pageNum最大是3,如果我们传5,理论上是返回空数组。
但
中有一个设定,“分页合理化,针对不合理的页码自动处理”,即,如果传pageNum为5,分页时其会按3来查.具体源码如下:
com.github.pagehelper.Page
public void setTotal(long total) {
this.total = total;
if (total == -1) {
pages = 1;
return;
}
if (pageSize > 0) {
pages = (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1));
} else {
pages = 0;
}
//分页合理化,针对不合理的页码自动处理
if ((reasonable != null && reasonable) && pageNum > pages) {
pageNum = pages;
calculateStartAndEndRow();
}
}
如果要修改,设置 page.setReasonable(false); 即可
参考:
mybatis的插件怎么做?
http://www.mybatis.org/mybatis-3/configuration.html#plugins
中文:
http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins
pageHelper官网
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/en/HowToUse.md