该项目的主repo已转移到 Meituan-Dianping仓库
zebra-dao is an asynchronous dao built on top of mybatis and mybatis-spring,it also supports page feature. Now, DianPing corp intenal has already using this dao on the product environment。
- support asynchronous methods, both
callbackandfuture - support paginate feature
- support all other
MyBatisfeatures.
Download the source code and compile.
git clone https://github.com/ainilife/zebra-dao.git
mvn clean install -DskipTests
Config pom.xml,add the following denpendcy. Also add Spring, Mybatis and Mybatis-Spring dependencies all by yourself.
<dependency>
<groupId>com.dianping.zebra</groupId>
<artifactId>zebra-dao</artifactId>
<version>0.1.5</version>
</dependency>
The only difference between original mybatis-spring config and zebra-dao is the following beans config.
<bean class="com.dianping.zebra.dao.mybatis.ZebraMapperScannerConfigurer">
<property name="basePackage" value="com.dianping.zebra.dao.mapper" />
<!--Optional,Default is 20 -->
<property name="initPoolSize" value="20"></property>
<!--Optional,Default is 200-->
<property name="maxPoolSize" value="200"></property>
<!--Optional,Default is 500-->
<property name="queueSize" value="500"></property>
</bean>
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/zebra?characterEncoding=UTF8"/>
<property name="user" value="admin"/>
<property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--dataource-->
<property name="dataSource" ref="datasource"/>
<!--Mapper files-->
<property name="mapperLocations" value="classpath*:config/sqlmap/**/*.xml" />
<!--Entity package-->
<property name="typeAliasesPackage" value="com.dianping.zebra.dao.entity" />
</bean>
1.For example, UserMapper.java has both synchronization and asynchronous methods.
public interface UserMapper {
/**
* Normal synchronization dao method.
*/
public UserEntity findUserById(@Param("userId") int userId);
/**
* Asynchronous callback method. Return void and only one
* callback method required.
*/
public void findUserById(@Param("userId") int userId, AsyncDaoCallback<UserEntity> callback);
}
2.AsyncDaoCallback implementation is as follow:
@Autowired
private UserMapper dao;
......
//asynchronous invoke
dao.findUserById(1, new AsyncDaoCallback<UserEntity>() {
@Override
public void onSuccess(UserEntity user) {
System.out.println(user);
//another asynchronous invoke in the asynchronous invoke
dao.findUserById(2, new AsyncDaoCallback<UserEntity>() {
@Override
public void onSuccess(UserEntity user) {
System.out.println(user);
}
@Override
public void onException(Exception e) {
}
});
//synchronization invoke in the asynchronous invoke
UserEntity entity = dao.findUserById(3);
System.out.println(entity);
}
@Override
public void onException(Exception e) {
}
});
3.Note that, asynchronous method should have the same name as the synchronization method. If you have to define a different name for
asynchronous method, you have to use annotation TargetMethod to define according synchronization method. For example:
//synchronization invoke
public UserEntity findUserById(@Param("userId") int userId);
//asynchronous invoke with a different method name
@TargetMethod(name = "findUserById")
public void findUserById2(@Param("userId") int userId, AsyncDaoCallback<UserEntity> callback);
1.For example,UserMapper.java has a synchronization method getAll. You can add a new method getAll1, also assign a synchronization method in the annotation TargetMethod 。
public interface UserMapper {
/**
* Normal synchronization dao method.
*/
public List<UserEntity> getAll();
/**
* Asynchronous future method. Return future and must have the
* same params as synchronization method.
*/
@TargetMethod(name = "getAll")
public Future<List<UserEntity>> getAll1();
}
2.Using Future to get data, for example:
@Autowired
private UserMapper dao;
......
Future<List<UserEntity>> future = dao.getAll1();
List<UserEntity> list = future.get();
for(UserEntity user : list){
System.out.println(user);
}
Logical Pagination loads all data from database and pagination in Java Code。In general, Java use JDBC cursor to position the ResultSet.MyBatis use this method to pagination.
In HeartbeatMapper.xml
<select id="getAll" parameterType="map" resultType="HeartbeatEntity">
SELECT * FROM heartbeat
</select>
In HeartbeatMapper.java, RowBounds can define both offset and limit:
List<HeartbeatEntity> getAll(RowBounds rb);
Physical Pagination loads paged data from database by using different sql. Different database vendor may has different implementation。zebra-dao implements an Interceptor of mybatis to achieve physical pagination feature. For example:
1.Modify the Spring bean sqlSessionFactory,add the configLocation
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:config/mybatis/mybatis-configuration.xml" />
</bean>
2.add mybatis-configuration.xml. Note that zebra-dao only implements MySQLDialect Now。
<?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>
<plugins>
<plugin interceptor="com.dianping.zebra.dao.plugin.page.PageInterceptor">
<property name="dialectClass" value="com.dianping.zebra.dao.dialect.MySQLDialect"/>
</plugin>
</plugins>
</configuration>
After these configuration, all page query become physical pagination.
zebra-dao supports get both totalRecord and records in one dao. For example:
In HeartbeatMapper.xml
<select id="getAll" parameterType="map" resultType="HeartbeatEntity">
SELECT * FROM heartbeat
</select>
In HeartbeatMapper.java, PageModel can define both page and pageSize. After the invoke, you can get both records and recordCount in the PageModel object. Note that to support this feature, physical pagination configs above is a must.
// must return void
void getAll(PageModel page);
1.By using RowBounds,both Callback and Future are supported.
2.By using PageModel,only support Callback style. Note that in Callback invoke, the result int onSuccess is not the real result.
dao.getAll(model, new AsyncDaoCallback<PageModel>() {
@Override
public void onSuccess(PageModel pageModel) {
//pageModel is null,real result is in the `model`
System.out.println(model.getRecordCount());
System.out.println(model.getRecords().size());
}
@Override
public void onException(Exception e) {
}
});