逆向工程就是,咱們根據數據的表,自動生成常見的Mapper映射接口和與數據庫表對應的pojo實體類,以及和Mapper接口對應的XML SQL語句,解放雙手不在去寫重複的代碼;好比對於每一個表根據主鍵id的查詢、更新、刪除、以及添加,這些常見的咱們都會去手寫。可是逆向工程就會把這些東西給我生成好,直接拿去用。php
github項目地址:github.com/fireshoot/G…java
我是在IDEA上寫的,其實和Eclipse差很少的,沒有什麼區別。mysql
另外我使用的方法是使用的mybatis官方的插件:mybatis-generator。linux
首先導入 mybatis-generator的依賴:git
<!-- 關於其餘數據庫依賴啥的我就不寫了-->
<!-- mybatis-generator逆向工程依賴-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
複製代碼
與數據的操做確定要先配置數據庫相關的參數:github
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/training?useSSL=false
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# mappers的位置
mybatis.mapper-locations=classpath:mappers/*.xml
# mybatis的基礎配置
mybatis.config-location=classpath:mybatis-config.xml
複製代碼
mybatis-config.xml中的配置文件:spring
<?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>
<settings>
<!-- 打開駝峯的配置 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 設置這個數據類型的別名,後面能夠用到-->
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer"/>
<typeAlias alias="Long" type="java.lang.Long"/>
<typeAlias alias="HashMap" type="java.util.HashMap"/>
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
<typeAlias alias="List" type="java.util.List"/>
<typeAlias alias="ArrayList" type="java.util.ArrayList"/>
<typeAlias alias="LinkedList" type="java.util.LinkedList"/>
</typeAliases>
</configuration>
複製代碼
我在resources資源文件夾下新建了generatorConfig.xml文件,這個文件主要就是配置的是與逆向工程相關的參數,我將配置的信息都寫了 註解:sql
<?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>
<!-- targetRuntime 要寫Mybatis3,寫其餘的估計會報錯,由於mysql如今的版本都很高了-->
<context id="DB2Tables" targetRuntime="Mybatis3">
<property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除自動生成的註釋 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--數據庫連接URL,用戶名、密碼 -->
<!-- nullCatalogMeansCurrent 在5.1.42中默認是true,而在6.0.6默認爲false。-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/training" userId="root" password="root">
</jdbcConnection>
<!-- 引入自定義類型轉換的實現類: 能夠不用寫 -->
<javaTypeResolver type="com.yangxin.demo.generator.MyJavaTypeResolver"></javaTypeResolver>
<!-- 設置Java類生成的位置 targetPackage:表示生成的路徑,targetProject:表示生成的文件/項目下 , 下面這個配置的意思就是:在src下生成com.yangxin.demo.dao.model-->
<javaModelGenerator targetPackage="com.yangxin.demo.dao.model" targetProject="src">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="false"/>
</javaModelGenerator>
<!-- 生成映射文件xml存放位置;targetPackage:本身的包名;targetProject:該文件你想放位置(路徑的不要有中文) -->
<sqlMapGenerator targetPackage="data.resources.mappers" targetProject="src">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成DAO的包名和位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.yangxin.demo.dao.mapper" targetProject="src">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 要生成的表 tableName是數據庫中的表名或視圖名 domainObjectName是實體類名-->
<table tableName="user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>
複製代碼
把generatorConfig.xml配置完成後,因而該進行下一步,寫生成的代碼,我新建了一個generator的包,而且新建了MybatisGeneratorApplication類具體以下:shell
public class MybatisGeneratorApplication {
private static Logger logger = LoggerFactory.getLogger(MybatisGeneratorApplication.class);
public static void main(String[] args) {
List<String> warnings = new ArrayList<>();
// 配置文件路徑
String xmlPath = "\\src\\main\\resources\\generatorConfig.xml";
try {
// System.getProperty("user.dir"),獲取當前的工做路徑,windows和linux裏的路徑格式不一致,這裏用來轉換,windows的是左斜槓,linux是右斜槓
String configFilePath = System.getProperty("user.dir").concat(
isNotWindows() ? xmlPath.replaceAll("\\\\", "/") : xmlPath);
logger.info("加載配置文件的路徑:" + configFilePath);
boolean overwrite = true;
File configFile = new File(configFilePath);
// configFile.exists():測試此抽象路徑名錶示的文件或目錄是否存在, isFile() :表示測試此抽象路徑名錶示的文件是不是一個標準文件。
logger.info("" + configFile.exists());
// 建立配置解析器
ConfigurationParser cp = new ConfigurationParser(warnings);
// 調用解析器建立配置對象()
Configuration config = cp.parseConfiguration(configFile);
// 建立一個ShellCallback對象,shellCallback接口是處理文件的建立和合並,默認是不支持文件合併的。
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
// 建立一個MyBatisGenerator對象。MyBatisGenerator類是真正用來執行生成動做的類
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
for (String wa : warnings) {
logger.info("warning:" + wa);
}
}
private static boolean isNotWindows() {
// System.getProperty("os.name") 獲取當前運行的系統名稱
return !System.getProperty("os.name").toLowerCase()
.startsWith("windows");
}
}
複製代碼
emmm... 我在數據庫建表的時候,有tinyInt類型的屬性,因而還新建了下面這個類,若是你沒有用到實際上是不用配置,不過我都貼出來:數據庫
public class MyJavaTypeResolver extends JavaTypeResolverDefaultImpl { /** * 將tinyint轉換爲Integer */ public MyJavaTypeResolver() { super(); super.typeMap.put(-6, new JdbcTypeInformation("TINYINT", new FullyQualifiedJavaType(Short.class.getName()))); } } 複製代碼
獲得的文件:
點開看看生成的是些什麼內容:
public interface UserMapper { int deleteByPrimaryKey(Integer id); int insert(User record); int insertSelective(User record); User selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); } 複製代碼
生成的Mapper接口,這6個就是咱們常見常常寫的,並且本身都不知道寫過多少遍的東西。
public class User { private Integer id; private String name; private Integer age; private Integer sex; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } 複製代碼
生成的實體類,也不用咱們本身寫,可是這個位置須要注意的是,若是生成的屬性名稱沒有成駝峯的形式,那麼你要檢查一下Mybatis的駝峯的配置是否存在或者有效。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.yangxin.demo.dao.mapper.UserMapper" > <resultMap id="BaseResultMap" type="com.yangxin.demo.dao.model.User" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="age" property="age" jdbcType="INTEGER" /> <result column="sex" property="sex" jdbcType="INTEGER" /> <result column="password" property="password" jdbcType="VARCHAR" /> </resultMap> <sql id="Base_Column_List" > id, name, age, sex, password </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from user where id = #{id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > delete from user where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="com.yangxin.demo.dao.model.User" > <selectKey resultType="java.lang.Integer" keyProperty="id" order="AFTER" > SELECT LAST_INSERT_ID() </selectKey> insert into user (name, age, sex, password) values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{sex,jdbcType=INTEGER}, #{password,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.yangxin.demo.dao.model.User" > <selectKey resultType="java.lang.Integer" keyProperty="id" order="AFTER" > SELECT LAST_INSERT_ID() </selectKey> insert into user <trim prefix="(" suffix=")" suffixOverrides="," > <if test="name != null" > name, </if> <if test="age != null" > age, </if> <if test="sex != null" > sex, </if> <if test="password != null" > password, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="name != null" > #{name,jdbcType=VARCHAR}, </if> <if test="age != null" > #{age,jdbcType=INTEGER}, </if> <if test="sex != null" > #{sex,jdbcType=INTEGER}, </if> <if test="password != null" > #{password,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.yangxin.demo.dao.model.User" > update user <set > <if test="name != null" > name = #{name,jdbcType=VARCHAR}, </if> <if test="age != null" > age = #{age,jdbcType=INTEGER}, </if> <if test="sex != null" > sex = #{sex,jdbcType=INTEGER}, </if> <if test="password != null" > password = #{password,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.yangxin.demo.dao.model.User" > update user set name = #{name,jdbcType=VARCHAR}, age = #{age,jdbcType=INTEGER}, sex = #{sex,jdbcType=INTEGER}, password = #{password,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper> 複製代碼
mapper的映射也生成好了的,這些都能大部分使用。
有可能會出現一些奇怪的問題通常以下:
1.你要生成的表 沒有主鍵id那麼,生成的東西就有問題
2.在配置生成表的屬性那兒,沒有配置徹底,有些方法會生成不全,只生成一部分
3.mysql版本引發的坑以下:springBoot自動添加的依賴是這樣,我由於這個問題致使生成的方法不全,缺乏一些東西。更改就是添加版本號就能夠了
4.其餘問題,多半仍是和配置有關,看看本身配置是否正確,也能夠去mybatis官網看官方文檔。
逆向工程幫咱們生成了這些東西,幫咱們作了不少工做,可是咱們會發現 在Mapper接口中,其實大多數表都是生成的這些東西,那麼咱們能夠將這些東西封裝一下嗎?
咱們封裝一個公共的接口 BaseMapper,由於在查詢更新的時候傳入實體類的值,BaseMapper接口設計確定要設計成一個泛型。
BaseMapper接口以下:
public interface BaseMapper<T extends BaseModel> {
int deleteByPrimaryKey(Integer id);
int insert(T record);
int insertSelective(T record);
T selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(T record);
int updateByPrimaryKey(T record);
}
複製代碼
那麼咱們就生成的接口中這樣以下圖操做:把生成的接口都去掉,那麼這個接口就空了,在這裏面咱們寫本身要寫的特列接口,特徵接口。
public interface UserMapper extends BaseMapper<User> {
// todo 添加特殊的表操做接口
}
複製代碼
在pojo的實體類中 繼承BaseModel
BaseModel類封裝了主鍵ID以下:
public class BaseModel {
protected Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
複製代碼
整體來看就簡潔了不少。
雖然逆向工程幫咱們生成了不少代碼,可是咱們還有不少相同的操做它沒有辦法生成下來:
①:咱們經過表的某一個字段的查詢。
②:咱們要對某一個字段進行模糊查詢 或者 in查詢
③:in查詢。
那麼咱們在BaseMapper中本身添加這些接口,咱們本身寫一個模板,之後就能夠直接複製粘貼使用了。
BaseMapper接口添加的以下:
public interface BaseMapper<T extends BaseModel> {
int deleteByPrimaryKey(Integer id);
int insert(T record);
int insertSelective(T record);
T selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(T record);
int updateByPrimaryKey(T record);
// conditions 這個參數,就是咱們傳進來的條件,在Sql中根據conditions這個list拼接sql語句
List<T> getByConditions(@Param("conditions") Map<String, Object> conditions);
// 同上:不過的是Condition類 其中有3個屬性:key opt value;key表示的是表的字段,opt表示 操做類型:locate模糊查詢、in查詢、爲空的時候就是等值查詢
List<T> getByConditionList(@Param("conditions") List<Condition> conditions);
// 同上 、 添加了 排序信息,Sort這個類有兩個參數 : field表示要排序的字段,sortType表示排序的方式
List<T> getSortedResultByConditionList(@Param("conditions") List<Condition> conditions, @Param("sorter") Sort sort);、
// field 表示要查詢的字段,set裏表示給值的內容
List<T> getByIn(@Param("field") String field, @Param("set") Set<Object> set);
}
複製代碼
Condition 封裝類:
public class Condition {
private String key;
/** * new Condition("account","like","%" + account + "%") new Condition("account","!=","account") */
private String opt;
private Object value;
public Condition(String key, String opt, Object value) {
this.key = key;
this.opt = opt;
this.value = value;
}
public Condition(String key, Object value) {
this(key, "=", value);
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getOpt() {
return opt;
}
public void setOpt(String opt) {
this.opt = opt;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
複製代碼
Sort封裝類:
public class Sort {
/** * 能夠field1,field2,field3, */
private String field;
/** * desc ,asc */
private String sortType;
public Sort(String field, String sortType) {
this.field = field;
this.sortType = sortType;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getSortType() {
return sortType;
}
public void setSortType(String sortType) {
this.sortType = sortType;
}
}
複製代碼
在BaseMapper中添加後,咱們在對應的.xml中添加 對應的接口映射,不過這裏涉及到了Mybatis的動態SqL,不會的同志去Mybatis官網看。
在咱們生成的mapper.xml中的最後的update後面添加:
<!-- 我就bb兩句邏輯吧: conditions中存儲的都是,key-value映射鍵值對,若是這個conditions不爲空,那麼咱們將這些條件都拼接,而且使用的and鏈接-->
<select id="getByConditions" parameterType="map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from user
<where>
<choose>
<when test="conditions!= null">
<!-- 循環拼接-->
<foreach close="" collection="conditions" index="key" item="value" open="" separator="AND">
${key}=#{value}
</foreach>
</when>
</choose>
</where>
</select>
<select id="getByConditionList" resultMap="BaseResultMap" parameterType="list">
select
<include refid="Base_Column_List"/>
from user
<where>
<choose>
<when test="conditions != null and conditions.size() > 0">
<foreach item="item" collection="conditions" open="" separator="AND" close="">
<choose>
<when test="item.opt == 'locate'">
locate(#{item.value}, ${item.key}) > 0
</when>
<when test="item.opt == 'in'">
${item.key} in
<foreach item="itemIn" collection="item.value" open="(" separator="," close=")">
#{itemIn}
</foreach>
</when>
<otherwise>
${item.key} ${item.opt} #{item.value}
</otherwise>
</choose>
</foreach>
</when>
</choose>
</where>
</select>
<select id="getSortedResultByConditionList" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from user
<where>
<choose>
<when test="conditions != null and conditions.size() > 0">
<foreach item="item" collection="conditions" open="" separator="AND" close="">
<choose>
<when test="item.opt != 'in'">
${item.key} ${item.opt} #{item.value}
</when>
<otherwise>
${item.key} ${item.opt} ${item.value}
</otherwise>
</choose>
</foreach>
</when>
</choose>
</where>
<choose>
<when test="sorter !=null">
order by ${sorter.field} ${sorter.sortType}
</when>
</choose>
</select>
<select id="getByIn" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
from user
<where>
<choose>
<when test="field != null">
${field} in
<foreach item="item" index="index" collection="set" open="(" separator="," close=")">
#{item}
</foreach>
</when>
</choose>
</where>
</select>
複製代碼
感受這樣就是一勞永逸的,說一說怎麼使用吧:以下:這個是list的,map的也是相似。
不過在複製粘貼到另外的地方使用的時候,要注意表名也要更改。
github地址:github.com/fireshoot/G…