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