一 背景介绍
本文主要是将dw-框架引入mybatis-plus。
二 依赖引入
这里采用Druid数据库连接池(可更换)
com.baomidou mybatis-plus3.3.2 com.alibaba druid1.1.8
三 mybatisBundle的加载
主要通过下面三个类来完成加载:
(1)MapperFactory主要负责Mapper文件的生成并注入HK2容器
(2)MybatisBundle负责与DW交互,让DW启动的时候加载MapperFactory
(3)SqlSessionTemplate主要是SqlSession的代理
详细介绍:
3.1 DW如何加载mybatis
DW自身有一个扩展点,可以通过实现ConfiguredBundle接口
public class AppInit implements IOCApplicationInitialization {
private static final Logger LOG = LoggerFactory.getLogger(AppInit.class);
@Override
public void initialize(Bootstrap bootstrap) {
List> entities = EntityScanner.doScan();
Objects.requireNonNull(entities, "Entity cant be null");
LOG.info("Urm contains entity domain total:{}", entities.size());
HibernateBundle hibernateBundle = new HibernateBundle(
ImmutableList.>builder().add(entities.toArray(new Class>[0])).build(),
new SessionFactoryFactory()) {
@Override
public PooledDataSourceFactory getDataSourceFactory(Configuration configuration) {
DataSourceConfig config = (DataSourceConfig) configuration;
return config.getDataSourceFactory("umebn_inventory_basic");
}
};
MybatisBundle mybatisBundle = new MybatisBundle() {
@Override
public DataSourceFactory getDataSourceFactory(Configuration configuration) {
DataSourceConfig config = (DataSourceConfig) configuration;
return config.getDataSourceFactory("umebn_inventory_basic");
}
};
HibernateHelper.setHibernateBundle(hibernateBundle);
bootstrap.addBundle(hibernateBundle);
bootstrap.addBundle(mybatisBundle);
}
}
3.2 MybtaisBundle实现
这里在run方法里面,会去执行 new MapperFactory().initDataSource(dbConfig),MapperFactory就加载对应的Mapper文件
public abstract class MybatisBundleimplements ConfiguredBundle { public final void run(T configuration, Environment environment) throws Exception { DataSourceFactory dbConfig = getDataSourceFactory(configuration); new MapperFactory().initDataSource(dbConfig); } protected abstract DataSourceFactory getDataSourceFactory(T configuration); }
3.3 MapperFactory的实现
MapperFactory有个核心的方法initDataSource(),这个方法里面主要做了以下几件事
(1)通过javaAF框架扫描到的database相关配置文件解析到的DataSourceFactory ,将DataSourceFactory 中的数据库相关封装成Properties ,提供给Druid
(2)Druid实现类创建DataSource
(3)通过DataSource创建Configuration (看过mybatis代码应该知道Configuration 在mybatis是一个全局配置类,非常重要)
(4)创建sqlSessionFactory
(5)创建sqlSession 代理(具体看3.4)
(6)将加载好的sqlSession 与sqlSessionFactory加入到HK2容器中
(7)扫描Mapper注解,通过sqlSession 生成Mapper动态代理类,将代理类注入到HK2容器,在Service进行DI的时候就可以直接成功注入。
public class MapperFactory {
public MapperFactory() {
}
private static DataSource dataSource = null;
private static SqlSessionFactory sqlSessionFactory = null;
private static Configuration configuration = null;
public void initDataSource(DataSourceFactory dataSourceFactory) {
Properties properties = parseDbProperties(dataSourceFactory);
try {
if (dataSource == null) {
dataSource = DruidDataSourceFactory.createDataSource(properties);
initSqlSessionFactory();
initMapper();
}
} catch (Exception e) {
log.error("Failed init dataSource", e);
}
}
private Properties parseDbProperties(DataSourceFactory dataSourceFactory) {
Properties properties = new Properties();
properties.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME, dataSourceFactory.getDriverClass());
properties.put(DruidDataSourceFactory.PROP_URL, dataSourceFactory.getUrl());
properties.put(DruidDataSourceFactory.PROP_USERNAME, dataSourceFactory.getUser());
properties.put(DruidDataSourceFactory.PROP_PASSWORD, dataSourceFactory.getPassword());
return properties;
}
private void initSqlSessionFactory() {
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
configuration = new MybatisConfiguration(environment);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
ServiceLocator serviceLocator = ServiceLocatorFactory.getInstance().find(ServiceUtils.DW_HK2);
if (serviceLocator == null) {
serviceLocator = ServiceLocatorFactory.getInstance().create(ServiceUtils.DW_HK2);
}
ServiceLocatorUtilities.addoneConstant(serviceLocator, sqlSessionFactory, "sqlSessionFactory", SqlSessionFactory.class);
ServiceLocatorUtilities.addoneConstant(serviceLocator, configuration, "configuration", Configuration.class);
}
private void initMapper() {
Set> classes = mapperScanner();
ServiceLocator serviceLocator = ServiceLocatorFactory.getInstance().find(ServiceUtils.DW_HK2);
classes.forEach(configuration::addMapper);
SqlSession sqlSession = new SqlSessionTemplate(sqlSessionFactory);
classes.forEach(clazz -> ServiceLocatorUtilities.addoneConstant(serviceLocator, sqlSession.getMapper(clazz), clazz.getName(), clazz));
}
private Set> mapperScanner() {
return ScannerUtils.scanByAnnotation(Mapper.class);
}
3.4 SqlSessionTemplate的实现(进一步优化)
这里主要就是实现一个动态代理
package com.zte.ums.ume.bn.inventory.otnresmgnt.uniresmanager.mybatis;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
public class SqlSessionTemplate implements SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class}, new SqlSessionInterceptor());
}
public SqlSessionFactory getSqlSessionFactory() {
return this.sqlSessionFactory;
}
public ExecutorType getExecutorType() {
return this.executorType;
}
@Override
public T selectOne(String statement) {
return this.sqlSessionProxy.selectOne(statement);
}
@Override
public T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);
}
@Override
public Map selectMap(String statement, String mapKey) {
return this.sqlSessionProxy.selectMap(statement, mapKey);
}
@Override
public Map selectMap(String statement, Object parameter, String mapKey) {
return this.sqlSessionProxy.selectMap(statement, parameter, mapKey);
}
@Override
public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
}
@Override
public Cursor selectCursor(String statement) {
return this.sqlSessionProxy.selectCursor(statement);
}
@Override
public Cursor selectCursor(String statement, Object parameter) {
return this.sqlSessionProxy.selectCursor(statement, parameter);
}
@Override
public Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
}
@Override
public List selectList(String statement) {
return this.sqlSessionProxy.selectList(statement);
}
@Override
public List selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
}
@Override
public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
}
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
}
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
}
@Override
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
@Override
public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
}
@Override
public int update(String statement) {
return this.sqlSessionProxy.update(statement);
}
@Override
public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
}
@Override
public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
}
@Override
public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
}
@Override
public T getMapper(Class type) {
return getConfiguration().getMapper(type, this);
}
@Override
public void commit() {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}
@Override
public void commit(boolean force) {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}
@Override
public void rollback() {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
@Override
public void rollback(boolean force) {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
@Override
public void close() {
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
}
@Override
public void clearCache() {
this.sqlSessionProxy.clearCache();
}
@Override
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
@Override
public Connection getConnection() {
return this.sqlSessionProxy.getConnection();
}
@Override
public List flushStatements() {
return this.sqlSessionProxy.flushStatements();
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = sqlSessionFactory.openSession(executorType, true);
try {
Object result = method.invoke(sqlSession, args);
sqlSession.commit(true);
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
sqlSession.rollback();
sqlSession = null;
throw unwrapped;
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
}
四 案例演示:
(1)实体类
@TableName("demo")
public class Demo {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(2)Rest接口
@Path("/xuyu")
@Contract
public interface DemoApi {
@POST
@Path("/add")
@Consumes({"application/json"})
@Produces({"application/json"})
@UnitOfWork
@io.swagger.annotations.ApiOperation(value = "", notes = "新增或修改基础信息", tags = {"OtnResMgntTopo",})
@io.swagger.annotations.ApiResponses(value = {
@io.swagger.annotations.ApiResponse(code = 200, message = "successful operation")})
Integer addOrUpdateBasicInfo(@ApiParam(value = "待新增或修改的基本信息", required = true) Demo demo
, @Context HttpServletRequest servletRequest)
throws Exception;
@GET
@Path("/find/{id}")
@Consumes({"application/json"})
@Produces({"application/json"})
@UnitOfWork
@io.swagger.annotations.ApiOperation(value = "", notes = "新增或修改基础信息", tags = {"OtnResMgntTopo"})
@io.swagger.annotations.ApiResponses(value = {
@io.swagger.annotations.ApiResponse(code = 200, message = "successful operation")})
Demo find(@PathParam(value = "id") String id
, @Context HttpServletRequest servletRequest)
throws Exception;
@POST
@Path("/delete/{id}")
@Consumes({"application/json"})
@Produces({"application/json"})
@UnitOfWork
@io.swagger.annotations.ApiOperation(value = "", notes = "新增或修改基础信息", tags = {"OtnResMgntTopo"})
@io.swagger.annotations.ApiResponses(value = {
@io.swagger.annotations.ApiResponse(code = 200, message = "successful operation")})
Integer delete(@PathParam(value = "id") String id
, @Context HttpServletRequest servletRequest)
throws Exception;
@POST
@Path("/select/{name}")
@Consumes({"application/json"})
@Produces({"application/json"})
@UnitOfWork
@io.swagger.annotations.ApiOperation(value = "", notes = "新增或修改基础信息", tags = {"OtnResMgntTopo"})
@io.swagger.annotations.ApiResponses(value = {
@io.swagger.annotations.ApiResponse(code = 200, message = "successful operation")})
List selectByName(@PathParam(value = "name") String name
, @Context HttpServletRequest servletRequest)
throws Exception;
}
(3)定义Mapper接口
这里findDemoByName是通过xml映射得到的,xml文件如下,注意namspace的写法要与接口全路径保持一致
这里额外补充:mybatis-plus自身提高了一些基础的功能在baseMapper里面,如果需要额外功能就需要自己写XML文件,以及通过注解,这三种方式
@Mapper public interface DemoMapper extends baseMapper{ List findDemoByName(String name); }
(4)Rest接口实现
@Service
public class DemoImpl implements DemoApi {
@Inject
private DemoMapper demoMapper;
@Override
public Integer addOrUpdateBasicInfo(Demo demo, HttpServletRequest servletRequest) throws Exception {
return demoMapper.insert(demo);
}
@Override
public Demo find(String id, HttpServletRequest servletRequest) throws Exception {
return demoMapper.selectById(id);
}
@Override
public Integer delete(String id, HttpServletRequest servletRequest) throws Exception {
return demoMapper.deleteById(id);
}
@Override
public List selectByName(String name, HttpServletRequest servletRequest) throws Exception {
return demoMapper.findDemoByName(name);
}
}
(5)接口访问与数据库
增删改查OK
六 后续改进:
(1)关于动态代理生成sqlSession那块,以及自动提交还需要改进
(2)关于mapper.xml文件的管理,需要统一起来,可以在application.yml进行配置



