讓MyBatis Generator產生的Mapper更簡潔

本文提供一種方法,目標是讓MyBatis Generator產生的Mapper更簡潔。html

主要體如今以下幾個方面:java

  • 有一個BaseMapper(本身編寫)
  • 全部產生的Mapper繼承BaseMapper無需每一個Mapper都要定義好多接口方法
  • 除了產生的Mapper有改動以外,其他自動產生的Entity、Example、XML文件保持不變

背景

不一樣Mapper的內容比較

好比,咱們創建兩個測試表,一個是t_user, 一個是t_news。其建表語句以下:sql

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.javaapache

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<User> 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.javaapi

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<News> 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<News> 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能夠看出,session

除了Entity、Entity對應的Example以及Primary Key可能會變化以外,其他全部的方法名都是同樣的。mybatis

分析

若是是這樣自動產生代碼,那麼各個Mapper勢必有不少重複的代碼,不直觀。app

那麼,問題來了?dom

可不能夠將這些通用的方法定義在一個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<T, E, PK extends Serializable> {

	long countByExample(E example);

	int deleteByExample(E example);

	int deleteByPrimaryKey(PK pk);

	int insert(T record);

	int insertSelective(T record);

	List<T> 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<News, NewsExample, Integer> {
}

接下來,咱們就來看看如何完成去達到這樣的目標。Let‘s GO~~~~

解決方法

改源代碼

改源代碼?

若是一個工具,讓產生的Dao繼承一個BaseMapper,都須要經過源碼來完成,那其擴展性可見通常。 不建議使用,這個只能是沒有辦法的時候纔會使用。

由於,上述考慮的都是Mapper,那麼,若是改動源代碼的話,咱們就在org.mybatis.generator.codegen.mybatis3.javamapper.JavaMapperGenerator類中修改便可。

修改包含兩個部分,

  • 不在Mapper中添加任何方法,由於這些都在BaseMapper中存在了,只要繼承便可。
  • 產生Mapper的時候,指定父類接口BaseMapper,  實體類類型、Example類型、主鍵類型。

具體在JavaMapperGenerator類的getCompilationUnits方法下進行:

public List<CompilationUnit> 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<String> 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

如:

<!-- 配置內置的或者自定義的Plugin -->
     <plugin type="my.mabatis.example.plugin.BaseMapperGeneratorPlugin" />

詳細配置如:

​
<?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>
	<!-- 引入配置文件 -->
	<properties resource="jdbc.properties" />

	<context id="context1" targetRuntime="MyBatis3">
		<!-- 配置內置的或者自定義的Plugin -->
		<plugin type="my.mabatis.example.plugin.BaseMapperGeneratorPlugin" />

		<!-- 註釋產生配置 -->
		<commentGenerator>
			<property name="suppressAllComments" value="false" />
			<property name="suppressDate" value="false" />
		</commentGenerator>

		<!-- 數據庫鏈接信息 -->
		<jdbcConnection driverClass="${jdbc.driverClassName}"
			connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}" />

		<!-- 生成Model對象路徑配置 -->
		<javaModelGenerator targetPackage="my.mybatis.generator.auto.entity"
			targetProject="src\main\java">
			<property name="enableSubPackages" value="true" />
			<property name="trimStrings" value="true" />
		</javaModelGenerator>

		<!-- 生成sqlXML文件路徑配置 -->
		<sqlMapGenerator targetPackage="my.mybatis.generator.auto.entity.xml"
			targetProject="src\main\java">
			<property name="enableSubPackages" value="true" />
		</sqlMapGenerator>

		<!-- 生成DAO的類文件路徑配置 -->
		<javaClientGenerator targetPackage="my.mybatis.generator.auto.dao"
			targetProject="src\main\java" type="XMLMAPPER">
			<property name="enableSubPackages" value="true" />
		</javaClientGenerator>

		<!--要生成哪些表 -->
		<table tableName="t_user" domainObjectName="User" />
		<table tableName="t_news" domainObjectName="News" />
	</context>
</generatorConfiguration>

​

通過上述幾個步驟,從新生成代碼,就能夠看到生成的Mapper,包括UserMapperNewsMapper都已經發生了變化,而這正是咱們所指望的。

public interface NewsMapper extends BaseMapper<News, NewsExample, Integer> {
}
public interface UserMapper extends BaseMapper<User, UserExample, Integer> {
}

自動產生的Mapper繼承於BaseMapper,變得相對較爲乾淨。

接下來,咱們就來測試一下,是否管用。

測試驗證

建立mybatis-config.xml文件

在src/main/resource目錄下建立mybatis-config.xml配置文件。內容以下:

<?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>
	<properties resource="jdbc.properties" />
	<typeAliases>
		<typeAlias type="my.mybatis.generator.auto.entity.User"
			alias="User" />
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="my/mybatis/generator/auto/entity/xml/UserMapper.xml" />
	</mappers>
</configuration>

建立一個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<User> 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("wmj123456@test.com");
		userToInsert.setName("mengjun");
		userService.insertUser(userToInsert);

		User user = userService.findUserByEmail("wmj123456@test.com");
		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.
wmj123456@test.com
mengjun

保存用戶信息和查看用戶信息都能成功執行,代碼可用。至此,整個流程就結束了。

Note:

整個代碼都是在上一篇文章<<使用MyBatis Generator自動生成代碼>>的基礎上改動的,如對generatorConfig.xml等配置文件或者對如何自動產生代碼有疑問,能夠參考一下。

工程結構

相關文章
相關標籤/搜索