MyBatis逆向工程

MyBatis逆向工程教程

前言

在学习与工作中,大家一定经常做 CRUD,也就是增删改查,而若要对一个表进行 CRUD 的操作,那么必须创建实体类、Dao 层接口、Dao 层实现类、SQL 语句。

如果你的项目使用了 MyBatis 的话,则还需要写一大堆与映射文件,作为一名菜鸟的我,简单的实体类不想写,自己设计的 Dao 接口功能不完善,最后还被复杂的映射文件整的晕头转向。

最终我使用了 MyBatis 逆向工程,才发现原本只会 CRUD 的我,又被冒犯到了,本来我也就只会创建几个实体类,实现几个简单的增删改查,写几个映射文件,最终发现,这些工作又被 MyBatis 逆向工程给抢了,虽然饭碗丢了,但还是觉得学会 MyBatis 逆向工程是真的香。

流程

学习一门技术,最好的方式就是上官网,虽然官网的英文,我大部分都是看不懂的(但是咱有翻译呀!)。

官网:

http://mybatis.org/generator/index.html

经过了一个下午的摸索,发现我这英语水平,看官网几乎不太可能完全看得懂的,所以最后我还是在度娘上找了许多各位前辈的经验分享,使用 MyBatis 逆向工程大致流程如下:

  • 创建 Maven 项目。
  • pom 文件导入官网提供的依赖。
  • 配置 jdbc.properties(数据库相关配置)。
  • 配置 mybatis-generator-config.xml。(参考官网)
  • 测试类执行 MyBatis 逆向工程。

源码

链接:https://pan.baidu.com/s/1Z9o8JuyRga-Q22TzPvztwg 提取码:643c

前期准备

创建数据表

-- 创建数据表(该表所属数据库为 haicoder_db) CREATE TABLE student( id INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(20), sex VARCHAR(5), age INT, address VARCHAR(30), email VARCHAR(50), phone VARCHAR(30), birthday DATE ) -- 插入一条记录 INSERT INTO student(`NAME`,sex,age,address,email,phone,birthday) VALUES( '张三','男',13,'广西','234433@qq.com','13223324535','1997-09-14');

创建Maven项目

新建模块,如下图:

17_MyBatis逆向工程.png

选择 Maven,点击 Next,如下图:

18_MyBatis逆向工程.png

填入项目信息,创建项目,如下图:

19_MyBatis逆向工程.png

项目结构,如下图:

20_MyBatis逆向工程.png

环境配置

MyBatis Generator 包括一个 Maven 插件,用于集成到 Maven 构建中,需要进行以下配置,如下图:

21_MyBatis逆向工程.png

pom 文件:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.haicoder</groupId> <artifactId>mybatis_generate</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--mybatis逆向工程核心包--> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.7</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--mybatis核心包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> </dependencies> <!--集成到Maven构建--> <build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <configurationFile>src/main/resources/mybatis-generator-config.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build> </project>

jdbc.properties 文件:

jdbc.path=D:/tools/maven/repository/mysql/mysql-connector-java/5.1.6/mysql-connector-java-5.1.6.jar jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/haicoder_db jdbc.username=root jdbc.password=root

mybatis-generator-config.xml 文件:

<?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"></properties> <!--jdbc驱动--> <classPathEntry location="${jdbc.path}"/> <context id="context" targetRuntime="MyBatis3"> <!--是否去除自动生成的注释--> <commentGenerator> <property name="suppressAllComments" value="true"/> <property name="suppressDate" value="true"/> </commentGenerator> <!--数据库连接的信息--> <jdbcConnection driverClass="${jdbc.driver}" connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}"/> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- targetPackage:实体类包名 targetProject:文件地址 --> <javaModelGenerator targetPackage="net.haicoder.domain" targetProject="E:\workspace\haicoder_javaee\mybatis_generate\src\main\java"> <property name="enableSubPackages" value="false"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- targetPackage:Mapper映射文件包名 targetProject:文件地址 --> <sqlMapGenerator targetPackage="net.haicoder.dao" targetProject="E:\workspace\haicoder_javaee\mybatis_generate\src\main\resources"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!--type="XMLMAPPER":生成XML映射文件和独立的持久层接口--> <javaClientGenerator targetPackage="net.haicoder.dao" targetProject="E:\workspace\haicoder_javaee\mybatis_generate\src\main\java" type="XMLMAPPER"> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- schema: 数据库名称 tableName: 生成的表名称 domainObjectName: 实体类名称 mapperName: Dao接口Dao映射文件名称 --> <table schema="haicoder_db" tableName="student" domainObjectName="Student" mapperName="StudentDao" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/> </context> </generatorConfiguration>

配置文件解析

通用配置部分,不进行赘述,如下图:

22_MyBatis逆向工程.png

自定义配置部分,配置部分解释如下:

标签 属性 作用
javaModelGenerator targetPackage 实体类生成后所属的包名
javaModelGenerator targetProject 实体类生成后的真实地址(建议使用绝对路径)
sqlMapGenerator targetPackage 实体类映射文件生成后所属的包名
sqlMapGenerator targetProject 实体类映射文件生成后的真实地址(建议使用绝对路径)
javaClientGenerator type=“XMLMAPPER” 生成 XML 映射文件和独立的持久层接口
table schema 数据库名称
table tableName 数据表名称
table domainObjectName 实体类名称
table mapperName Dao 接口,Dao 映射文件名称

代码实现

测试类:

package net.haicoder; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback; import java.io.InputStream; import java.util.ArrayList; import java.util.List; public class MyBatisGeneratorTest { public void generator() throws Exception { List<String> warnings = new ArrayList<>(); boolean overwrite = true; // 指定逆向工程配置文件 InputStream in = MyBatisGeneratorTest.class.getClassLoader().getResourceAsStream("mybatis-generator-config.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(in); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); in.close(); } public static void main(String[] args) { System.out.println("嗨客网(www.haicoder.net)\n"); try { MyBatisGeneratorTest myBatisGenerator = new MyBatisGeneratorTest(); myBatisGenerator.generator(); System.out.println("生成完毕!"); } catch (Exception e) { e.printStackTrace(); } } }

执行 MyBatisGeneratorTest 类,运行结果如下:

23_MyBatis逆向工程.png

此时发现已经生成了实体类、Dao接口、实体映射文件,如下图:

24_MyBatis逆向工程.png

增删改查

前期准备

为了测试学生表的增删改查操作,此处添加 junit 依赖,pom 文件修改如下:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.haicoder</groupId> <artifactId>mybatis_generate</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--mybatis逆向工程核心包--> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.7</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--mybatis核心包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <!--Maven的目标与执行--> <build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <configurationFile>src/main/resources/mybatis-generator-config.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build> </project>

创建 MyBatis 核心配置文件,代码如下:

<?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> <!--在控制台显示SQL语句--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--定义实体类别名--> <typeAliases> <package name="net.haicoder.domain"/> </typeAliases> <environments default="default"> <!--环境变量--> <environment id="default"> <!--事务管理器--> <transactionManager type="JDBC"/> <!--数据源--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/haicoder_db"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--加载其它映射文件--> <mappers> <package name="net.haicoder.dao"/> </mappers> </configuration>

测试代码

package net.haicoder.test; import net.haicoder.dao.StudentDao; import net.haicoder.domain.Student; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; /** * 测试类 */ public class TestStudentMapper { // 只需要创建一次 private static SqlSessionFactory factory; // 声明会话对象 private SqlSession session; // 声明DAO接口 private StudentDao studentDao; // 静态方法,类加载完毕以后执行1次 @BeforeClass public static void init() throws IOException { // 创建核心配置文件的输入流 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); // 创建工厂建造类 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); factory = builder.build(resourceAsStream); } @Before public void begin() { session = factory.openSession(); //通过会话得到代理对象 studentDao = session.getMapper(StudentDao.class); } // 根据主键查询学生信息 @Test public void testSelectByPrimaryKey() { System.out.println("嗨客网(www.haicoder.net)\n"); Student student = studentDao.selectByPrimaryKey(1); System.out.println("【学生信息】:" + student + "\n"); } // 添加学生信息 @Test public void testInsert() { System.out.println("嗨客网(www.haicoder.net)\n"); Student student = new Student(); student.setId(null); student.setName("李四"); student.setSex("男"); student.setAge(20); student.setAddress("上海"); student.setEmail("13343328@haicoder.net"); student.setPhone("13332247432"); studentDao.insert(student); System.out.println("添加成功!" ); } // 选择性添加学生信息 @Test public void testInsertSelective() { System.out.println("嗨客网(www.haicoder.net)\n"); Student student = new Student(); student.setName("王五"); student.setSex("男"); student.setAge(20); student.setAddress("上海"); student.setEmail("13343328@haicoder.net"); studentDao.insertSelective(student); System.out.println("添加成功!" ); } // 根据主键更新学生信息 @Test public void testUpdateByPrimaryKey() { System.out.println("嗨客网(www.haicoder.net)\n"); Student student = new Student(); student.setId(3); student.setName("王五"); student.setSex("男"); student.setAge(25); student.setPhone("1323423234"); student.setBirthday(new Date()); studentDao.updateByPrimaryKey(student); System.out.println("更新成功!" ); } // 根据主键选择性更新学生信息 @Test public void testUpdateByPrimaryKeySelective() { System.out.println("嗨客网(www.haicoder.net)\n"); Student student = new Student(); student.setId(3); student.setAddress("上海"); student.setEmail("13343328@haicoder.net"); studentDao.updateByPrimaryKeySelective(student); System.out.println("更新成功!" ); } @After public void end() { // 手动提交事务 session.commit(); session.close(); } }

根据主键查询

测试前,查看数据库数据,如下图:

25_MyBatis逆向工程.png

根据主键查询数据,运行结果如下图:

26_MyBatis逆向工程.png

添加学生信息

添加学生所有信息,运行结果如下图:

27_MyBatis逆向工程.png

添加成功后,查看数据库,如下图:

28_MyBatis逆向工程.png

选择性添加学生信息(常用),运行结果如下图:

29_MyBatis逆向工程.png

添加成功后,查看数据库,如下图:

30_MyBatis逆向工程.png

观察上方两种添加方式的 SQL 语句,可以得出以下结论:

  • insert:所有字段添加,若该字段无数据,则会自动补 NULL,然后存放数据。
  • insertSelective:只添加有数据的字段,若该字段无数据,则会存放该字段的默认值。
  • 因此,insertSelectiveinsert 要更加灵活。

根据主键更新

根据主键更新学生所有信息,运行结果如下图:

31_MyBatis逆向工程.png

更新成功后,查看数据库变化,如下图:

32_MyBatis逆向工程.png

根据主键更新学生部分信息(常用),运行结果如下图:

33_MyBatis逆向工程.png

更新成功后,查看数据库,如下图:

34_MyBatis逆向工程.png

观察上方两种更新方式的 SQL 语句,可以得出以下结论:

  • updateByPrimaryKey:所有字段更新,若该字段无数据,则会自动更新为 NULL,然后更新数据。(可能会造成原本数据丢失)
  • updateByPrimaryKeySelective:只更新有数据的字段,若该字段无数据,则不会进行更新。
  • 因此,updateByPrimaryKeySelectiveupdateByPrimaryKey 要更加灵活。

条件查询

不会吧,不会有人以为到这里已经要结束了吧?那你可真的小看 MyBatis 逆向工程了。看了上面的案例后,你可能觉得少了点啥,但一时又不知道少了什。试想一下,我们平时查询时,还有根据性别查询学生,查询姓张的学生,查询未成年的学生等等,反观上面案例只有剩一个主键查询了,难道是作者忘记设计了?下面开始演示根据条件查询学生信息。(试想一下,我们平时是不是根据条件查询才是最多的操作,所以这点很重要,请耐心读完!)

前期准备

为了使用条件查询,我们需要修改配置文件 mybatis-generator-config.xml,如下图:

35_MyBatis逆向工程.png

整天想偷懒的我,同样理解你们,C、V 大法操作起来,代码如下:

<?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"></properties> <!--jdbc驱动--> <classPathEntry location="${jdbc.path}"/> <context id="context" targetRuntime="MyBatis3"> <!--自动生成toString--> <plugin type="org.mybatis.generator.plugins.ToStringPlugin" /> <!--是否去除自动生成的注释--> <commentGenerator> <property name="suppressAllComments" value="true"/> <property name="suppressDate" value="true"/> </commentGenerator> <!--数据库连接的信息--> <jdbcConnection driverClass="${jdbc.driver}" connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}"/> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- targetPackage:实体类包名 targetProject:文件地址 --> <javaModelGenerator targetPackage="net.haicoder.domain" targetProject="E:\workspace\haicoder_javaee\mybatis_generate\src\main\java"> <property name="enableSubPackages" value="false"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- targetPackage:Mapper映射文件包名 targetProject:文件地址 --> <sqlMapGenerator targetPackage="net.haicoder.dao" targetProject="E:\workspace\haicoder_javaee\mybatis_generate\src\main\resources"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!--type="XMLMAPPER":生成XML映射文件和独立的持久层接口--> <javaClientGenerator targetPackage="net.haicoder.dao" targetProject="E:\workspace\haicoder_javaee\mybatis_generate\src\main\java" type="XMLMAPPER"> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- schema: 数据库名称 tableName: 生成的表名称 domainObjectName: 实体类名称 mapperName: Dao接口Dao映射文件名称 --> <table schema="haicoder_db" tableName="student" domainObjectName="Student" mapperName="StudentDao" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"/> </context> </generatorConfiguration>

此时需要重新再生成一次代码,如下图:

36_MyBatis逆向工程.png

生成之后,你一定充满了疑问,怎么 StudentDao.xml 全都是红色的?难道显卡坏掉了?当然不是,因为再次生成,它是直接继续在原本的实体类映射文件中生成的,因此出现了大量的重复代码,注意:重新生成前,需要把原本的实体映射文件删除,再重新生成,再次执行 MyBatisGeneratorTest,结果如下:

37_MyBatis逆向工程.png

生成成功后,新的项目结构如下图:

38_MyBatis逆向工程.png

此时发现目录中比原本的多了个类,这个类就是帮助我们条件查询的。

测试代码

package net.haicoder.test; import net.haicoder.dao.StudentDao; import net.haicoder.domain.Student; import net.haicoder.domain.StudentExample; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 测试类 */ public class TestStudentMapper { // 只需要创建一次 private static SqlSessionFactory factory; // 声明会话对象 private SqlSession session; // 声明DAO接口 private StudentDao studentDao; // 静态方法,类加载完毕以后执行1次 @BeforeClass public static void init() throws IOException { // 创建核心配置文件的输入流 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); // 创建工厂建造类 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); factory = builder.build(resourceAsStream); } @Before public void begin() { session = factory.openSession(); //通过会话得到代理对象 studentDao = session.getMapper(StudentDao.class); } // 查询所有学生信息 @Test public void testFindAllStudent() { System.out.println("嗨客网(www.haicoder.net)\n"); StudentExample studentExample = new StudentExample(); List<Student> studentList = studentDao.selectByExample(studentExample); for(Student student:studentList){ System.out.println(student); } } // 查询来自上海的学生 @Test public void testFindStudentByAddress() { System.out.println("嗨客网(www.haicoder.net)\n"); StudentExample studentExample = new StudentExample(); // 条件:address='上海' studentExample.createCriteria().andAddressEqualTo("上海"); List<Student> studentList = studentDao.selectByExample(studentExample); for(Student student:studentList){ System.out.println(student); } } // 查询姓张的学生 @Test public void testFindStudentBySurname() { System.out.println("嗨客网(www.haicoder.net)\n"); StudentExample studentExample = new StudentExample(); // 条件:name like '%张%' studentExample.createCriteria().andNameLike("%张%"); List<Student> studentList = studentDao.selectByExample(studentExample); for(Student student:studentList){ System.out.println(student); } } // 按照年龄倒叙查询学生 @Test public void testFindStudentByAge() { System.out.println("嗨客网(www.haicoder.net)\n"); StudentExample studentExample = new StudentExample(); // 条件:order by age desc studentExample.setOrderByClause("age desc"); List<Student> studentList = studentDao.selectByExample(studentExample); for(Student student:studentList){ System.out.println(student); } } @After public void end() { // 手动提交事务 session.commit(); session.close(); } }

运行结果

查询所有学生信息,运行结果如下:

39_MyBatis逆向工程.png

查询来自上海的学生,运行结果如下:

40_MyBatis逆向工程.png

查询姓张的学生,运行结果如下:

41_MyBatis逆向工程.png

按照年龄倒叙查询学生,运行结果如下:

42_MyBatis逆向工程.png

总结

以上案例运用了 MyBatis 逆向工程的技术,生成了实体类、Dao 接口、实体类映射文件,并演示了简单的增删改查以及条件查询等操作。