2019独角兽企业重金招聘Python工程师标准>>>
本文提供一种方法,目标是让MyBatis Generator产生的Mapper更简洁。
主要体现在如下几个方面:
- 有一个BaseMapper(自己编写)
- 所有产生的Mapper继承BaseMapper,无需每个Mapper都要定义好多接口方法
- 除了产生的Mapper有改动之外,其余自动产生的Entity、Example、XML文件保持不变
背景
不同Mapper的内容比较
比如,我们建立两个测试表,一个是t_user, 一个是t_news。其建表语句如下:
CREATE TABLE `t_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(64) DEFAULT NULL,
`name` varchar(30) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `t_news` (
`news_id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(150) NOT NULL,
`content` text NOT NULL,
`brief_intro` varchar(255) DEFAULT NULL,
`pic_url` varchar(255) DEFAULT NULL,
`news_from` varchar(100) DEFAULT NULL,
`news_author` varchar(50) DEFAULT NULL,
`news_url` varchar(255) DEFAULT NULL,
`keywords` varchar(150) DEFAULT NULL,
`meta_desc` varchar(150) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`news_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
然后,我们比较一下自动产生的Mapper有什么区别?
UserMapper.java
import java.util.List;
import my.mybatis.generator.auto.entity.User;
import my.mybatis.generator.auto.entity.UserExample;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int countByExample(UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int deleteByExample(UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int deleteByPrimaryKey(Integer userId);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int insert(User record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int insertSelective(User record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
List selectByExample(UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
User selectByPrimaryKey(Integer userId);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByExample(@Param("record") User record, @Param("example") UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByPrimaryKeySelective(User record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_user
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByPrimaryKey(User record);
}
NewsMapper.java
import java.util.List;
import my.mybatis.generator.auto.entity.News;
import my.mybatis.generator.auto.entity.NewsExample;
import org.apache.ibatis.annotations.Param;
public interface NewsMapper {
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int countByExample(NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int deleteByExample(NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int deleteByPrimaryKey(Integer newsId);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int insert(News record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int insertSelective(News record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
List selectByExampleWithBLOBs(NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
List selectByExample(NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
News selectByPrimaryKey(Integer newsId);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByExampleSelective(@Param("record") News record, @Param("example") NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByExampleWithBLOBs(@Param("record") News record, @Param("example") NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByExample(@Param("record") News record, @Param("example") NewsExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByPrimaryKeySelective(News record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByPrimaryKeyWithBLOBs(News record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table m_news
*
* @mbggenerated Wed Nov 09 10:23:34 CST 2016
*/
int updateByPrimaryKey(News record);
}
从上述两个自动产生的Mapper可以看出,
除了Entity、Entity对应的Example以及Primary Key可能会变化之外,其余所有的方法名都是一样的。
分析
如果是这样自动产生代码,那么各个Mapper势必有很多重复的代码,不直观。
那么,问题来了?
可不可以将这些通用的方法定义在一个BaseMapper中,然后,其余自动产生的Mapper继承自BaseMapper,且与各自的Entity、Example、Primary KEY绑定在一起呢?
比如:
定义一个BaseMapper.java,其中,
- T表示与table表对应的实体类(Entity)
- E表示Entity对应的Example类
- PK表示可能会用到主键 (比如Integer等)
如下所示:
package my.mabatis.example.base;
import java.io.Serializable;
import java.util.List;
import org.apache.ibatis.annotations.Param;
/**
*
* @author wangmengjun
*
*/
public interface BaseMapper {
long countByExample(E example);
int deleteByExample(E example);
int deleteByPrimaryKey(PK pk);
int insert(T record);
int insertSelective(T record);
List selectByExample(E example);
T selectByPrimaryKey(PK pk);
int updateByExampleSelective(@Param("record") T record,
@Param("example") E example);
int updateByExample(@Param("record") T record, @Param("example") E example);
int updateByPrimaryKeySelective(T record);
int updateByPrimaryKey(T record);
}
那么,
UserMapper.java就变成类似如下的样子了。
public interface NewsMapper extends BaseMapper {
}
接下来,我们就来看看如何完成去达到这样的目标。Let‘s GO~~~~
解决方法
改源代码
改源代码?
如果一个工具,让产生的Dao继承一个BaseMapper,都需要通过源码来完成,那其扩展性可见一般。 不建议使用,这个只能是没有办法的时候才会使用。
因为,上述考虑的都是Mapper,那么,如果改动源代码的话,我们就在org.mybatis.generator.codegen.mybatis3.javamapper.JavaMapperGenerator类中修改即可。
修改包含两个部分,
- 不在Mapper中添加任何方法,因为这些都在BaseMapper中存在了,只要继承即可。
- 产生Mapper的时候,指定父类接口BaseMapper, 实体类类型、Example类型、主键类型。
具体在JavaMapperGenerator类的getCompilationUnits方法下进行:
public List getCompilationUnits() {
//省略所有方法内容
}
- 移除添加方法的代码
addCountByExampleMethod(interfaze);
addDeleteByExampleMethod(interfaze);
addDeleteByPrimaryKeyMethod(interfaze);
addInsertMethod(interfaze);
addInsertSelectiveMethod(interfaze);
addSelectByExampleWithBLOBsMethod(interfaze);
addSelectByExampleWithoutBLOBsMethod(interfaze);
addSelectByPrimaryKeyMethod(interfaze);
addUpdateByExampleSelectiveMethod(interfaze);
addUpdateByExampleWithBLOBsMethod(interfaze);
addUpdateByExampleWithoutBLOBsMethod(interfaze);
addUpdateByPrimaryKeySelectiveMethod(interfaze);
addUpdateByPrimaryKeyWithBLOBsMethod(interfaze);
addUpdateByPrimaryKeyWithoutBLOBsMethod(interfaze);
- 指定父类接口BaseMapper, 实体类类型等
在上述移除的代码块中添加类似如下代码块。
/**
* 主键默认采用java.lang.Integer
*/
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType("BaseMapper<"
+ introspectedTable.getBaseRecordType() + ","
+ introspectedTable.getExampleType() + ","
+ "java.lang.Integer" + ">");
FullyQualifiedJavaType imp = new FullyQualifiedJavaType(
"my.mabatis.example.base.BaseMapper");
/**
* 添加 extends MybatisBaseMapper
*/
interfaze.addSuperInterface(fqjt);
/**
* 添加import my.mabatis.example.base.MybatisBaseMapper;
*/
interfaze.addImportedType(imp);
/**
* 方法不需要
*/
interfaze.getMethods().clear();
然后,然后就搞定了。 : )
不改源代码?
尽管修改源代码的方式可行,但是侵入性太强。
其实,MyBatis Generator自动代码产生工具已经提供插件适配扩展的功能,我们只要继承PluginAdapter即可。然后,重写clientGenerated方法即可。
@Override
public boolean clientGenerated(Interface interfaze,
TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
}
具体代码如下:
默认采用java.lang.Integer作为主键。
package my.mabatis.example.plugin;
import java.util.List;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.TopLevelClass;
/**
* @author wangmengjun
*
*/
public class BaseMapperGeneratorPlugin extends PluginAdapter {
public boolean validate(List warnings) {
return true;
}
/**
* 生成dao
*/
@Override
public boolean clientGenerated(Interface interfaze,
TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
/**
* 主键默认采用java.lang.Integer
*/
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType("BaseMapper<"
+ introspectedTable.getBaseRecordType() + ","
+ introspectedTable.getExampleType() + ","
+ "java.lang.Integer" + ">");
FullyQualifiedJavaType imp = new FullyQualifiedJavaType(
"my.mabatis.example.base.BaseMapper");
/**
* 添加 extends MybatisBaseMapper
*/
interfaze.addSuperInterface(fqjt);
/**
* 添加import my.mabatis.example.base.MybatisBaseMapper;
*/
interfaze.addImportedType(imp);
/**
* 方法不需要
*/
interfaze.getMethods().clear();
interfaze.getAnnotations().clear();
return true;
}
}
接着,在用于自动产生代码的配置文件中generatorConfig.xml指定自定义的plugin。
如:
详细配置如:
经过上述几个步骤,重新生成代码,就可以看到生成的Mapper,包括UserMapper和NewsMapper都已经发生了变化,而这正是我们所期望的。
public interface NewsMapper extends BaseMapper {
}
public interface UserMapper extends BaseMapper {
}
自动产生的Mapper继承于BaseMapper,变得相对较为干净。
接下来,我们就来测试一下,是否管用。
测试验证
创建mybatis-config.xml文件
在src/main/resource目录下创建mybatis-config.xml配置文件。内容如下:
创建一个MyBatisUtil工具类
package my.mabatis.example.util;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
*
* @author wangmengjun
*
*/
public class MyBatisUtil {
private static SqlSessionFactory factory;
private MyBatisUtil() {
}
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
factory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSessionFactory getSqlSessionFactory() {
return factory;
}
}
编写Service类
因为只是很简单的操作,所以service就不分接口和实现了,直接上代码。
package my.mabatis.example.service;
import java.util.List;
import my.mabatis.example.util.MyBatisUtil;
import my.mybatis.generator.auto.dao.UserMapper;
import my.mybatis.generator.auto.entity.User;
import my.mybatis.generator.auto.entity.UserExample;
import my.mybatis.generator.auto.entity.UserExample.Criteria;
import org.apache.ibatis.session.SqlSession;
/**
*
* @author wangmengjun
*
*/
public class UserService {
/**
* 保存用户
* @param user 待保存用户对象
*/
public void insertUser(User user) {
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
.openSession();
try {
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.insert(user);
sqlSession.commit();
} finally {
sqlSession.close();
}
}
/**
* 按照指定email返回用户
* @param email
* @return 按照指定email返回用户
*/
public User findUserByEmail(String email) {
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
.openSession();
try {
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
/**
* 使用Example来操作
*/
UserExample example = new UserExample();
Criteria criteria = example.createCriteria();
criteria.andEmailEqualTo(email);
List users = userDao.selectByExample(example);
/**
* 假定email唯一
*/
return users.isEmpty() ? null : users.get(0);
} finally {
sqlSession.close();
}
}
}
测试类和运行结果
package my.mabatis.example.runner;
import my.mabatis.example.service.UserService;
import my.mybatis.generator.auto.entity.User;
public class Test {
public static void main(String[] args) {
UserService userService = new UserService();
User userToInsert = new User();
userToInsert.setEmail("[email protected]");
userToInsert.setName("mengjun");
userService.insertUser(userToInsert);
User user = userService.findUserByEmail("[email protected]");
System.out.println(user.getEmail());
System.out.println(user.getName());
}
}
输出结果:
log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[email protected]
mengjun
保存用户信息和查看用户信息都能成功执行,代码可用。至此,整个流程就结束了。
Note:
整个代码都是在上一篇文章<<使用MyBatis Generator自动生成代码>>的基础上改动的,如对generatorConfig.xml等配置文件或者对如何自动产生代码有疑问,可以参考一下。
工程结构