MyBatis学习总结(四)——MyBatis缓存与代码生成
一、MyBatis缓存
缓存可以提高系统性能,可以加快访问速度,减轻服务器压力,带来更好的用户体验。缓存用空间换时间,好的缓存是缓存命中率高的且数据量小的。缓存是一种非常重要的技术。
1.0、再次封装SqlSessionFactoryUtils
为了配置缓存的学习我们将工具类再次封装。
原SqlSessionFactoryUtil工具类如下:
package com.zhangguo.mybatis03.utils; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; /** * MyBatis 会话工具类 * */ public class SqlSessionFactoryUtil { /** * 获得会话工厂 * * */ public static SqlSessionFactory getFactory(){ InputStream inputStream = null; SqlSessionFactory sqlSessionFactory=null; try{ //加载conf.xml配置文件,转换成输入流 inputStream = SqlSessionFactoryUtil.class.getClassLoader().getResourceAsStream("mybatisCfg.xml"); //根据配置文件的输入流构造一个SQL会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } finally { if(inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return sqlSessionFactory; } /** * 获得sql会话,是否自动提交 * */ public static SqlSession openSession(boolean isAutoCommit){ return getFactory().openSession(isAutoCommit); } /** * 关闭会话 * */ public static void closeSession(SqlSession session){ if(session!=null){ session.close(); } } }
上面的代码中当我们每次获取SQLSession时都要实例化sqlSessionFactory,效率不高。可以使用单例改进:
package com.zhangguo.mybatis03.utils; 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 java.io.IOException; import java.io.InputStream; /** * MyBatis会话工具类 * */ public class SqlSessionFactoryUtils { /**会话工厂*/ private static SqlSessionFactory factory; static { try { /*获得配置文件的文件流*/ InputStream inputStream=Resources.getResourceAsStream("mybatisCfg.xml"); //初始化工厂 factory=new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /** * 获得会话对象 * 指定是否自动提交 * */ public static SqlSession openSqlSession(boolean isAutoCommit){ return getFactory().openSession(isAutoCommit); } public static SqlSessionFactory getFactory() { return factory; } public static void setFactory(SqlSessionFactory factory) { SqlSessionFactoryUtils.factory = factory; } /** * 关闭会话 * */ public static void closeSession(SqlSession session){ if(session!=null){ session.close(); } } }
1.1、MyBatis缓存概要
在一个系统中查询的频次远远高于增删改,据三方统计不同系统比例在9:1-7:3之间。正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持
(1)、一级缓存基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
(2)、二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
(3)、对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
1.2、默认MyBatis的一级缓存是开启的
测试用例:
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); Student student2 = mapper1.selectStudentById(1); System.out.println(student2); //关闭 SqlSessionFactoryUtils.closeSession(session1); }
结果:
虽然查询了二次,但只向数据库发送了一次SQL请求,因为第二次是在缓存中获得的数据。
1.3、一级缓存仅在同一个会话(SQLSession)中有效
测试用例:
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话1,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); //打开一个会话2,不自动提交 SqlSession session2 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器 StudentMapper mapper2 = session2.getMapper(StudentMapper.class); Student student2 = mapper2.selectStudentById(1); System.out.println(student2); //关闭 SqlSessionFactoryUtils.closeSession(session1); }
结果:
从上图可以看出此时并没有使用缓存,向数据库查询了二次,因为第二次查询使用的是新的会话,而一级缓存必须在同一个会话中。
1.4、清空一级缓存
(1)、当对表执行增删改时缓存将清空
测试用例:
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话1,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); //执行更新 Student lili=new Student(); lili.setId(5); lili.setSex("girl"); mapper1.updateStudent(lili); Student student2 = mapper1.selectStudentById(1); System.out.println(student2); SqlSessionFactoryUtils.closeSession(session1); }
结果:
从日志中可以看出第二次查询也发送了sql到数据库中,并没有使用缓存,是因为执行了更新操作缓存已被清空。
此时数据库中的数据其实并未真的更新,如下所示:
因为没有手动提交,可以设置自动提交
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话1,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); //执行更新 Student lili=new Student(); lili.setId(5); lili.setSex("girl"); mapper1.updateStudent(lili); Student student2 = mapper1.selectStudentById(1); System.out.println(student2); //提交 session1.commit(); SqlSessionFactoryUtils.closeSession(session1); }
提交后的结果
(2)、手动清空
测试用例:
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话1,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); //执行手动更新 session1.clearCache(); Student student2 = mapper1.selectStudentById(1); System.out.println(student2); //提交 session1.commit(); SqlSessionFactoryUtils.closeSession(session1); }
结果:
从日志中可以看到第二次查询并未使用缓存因为执行了手动清空缓存,没有缓存可用则再次查询数据库。
小结:当Session flush或close之后,该Session中的所有 Cache 就将清空;执行CUD也将会自动清空;手动清空;
1.5、开启二级缓存
默认二级缓存是不开启的,需要手动进行配置:
1.5.1、全局开关
默认是true,如果它配成false,其余各个Mapper XML文件配成支持cache也没用。
<settings> <!--设置是否允许缓存--> <setting name="cacheEnabled" value="true"/> <!--设置日志输出的目标--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
1.5.2、单个Mapper XML映射文件开关
默认Mapper XML映射文件是不采用cache。在配置文件加一行就可以支持cache:
<?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.zhangguo.mybatis03.dao.StudentMapper"> <cache/> <select id="selectStudentById" resultType="Student"> SELECT id,name,sex from student where id=#{id} </select> </mapper>
可以在开启二级缓存时候,手动配置一些属性
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
各个属性意义如下:
- eviction:缓存回收策略
- LRU:最少使用原则,移除最长时间不使用的对象
- FIFO:先进先出原则,按照对象进入缓存顺序进行回收
- SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用,更积极的移除移除基于垃圾回收器状态和弱引用规则的对象 - flushInterval:刷新时间间隔,单位为毫秒,这里配置的100毫秒。如果不配置,那么只有在进行数据库修改操作才会被动刷新缓存区
- size:引用额数目,代表缓存最多可以存储的对象个数
- readOnly:是否只读,如果为true,则所有相同的sql语句返回的是同一个对象(有助于提高性能,但并发操作同一条数据时,可能不安全),如果设置为false,则相同的sql,后面访问的是cache的clone副本。
1.5.3、Mapper statement开关
<select id="selectStudentById" resultType="Student" useCache="false"> SELECT id,name,sex from student where id=#{id} </select>
可以在Mapper的具体方法下设置对二级缓存的访问意愿:
-
useCache配置
如果一条语句每次都需要最新的数据,就意味着每次都需要从数据库中查询数据,可以把这个属性设置为false,如:
<select id="selectAll" useCache="false">
-
刷新缓存(就是清空缓存)
二级缓存默认会在insert、update、delete操作后刷新缓存,可以手动配置不更新缓存,如下:
<update id="updateById" flushCache="false" />
1.5.4、实现可序列化接口
如果未实现可序列化接口,会引发异常。
修改POJO对象,增加实现可序列化接口:
package com.zhangguo.mybatis03.entities; import java.io.Serializable; /** * 学生实体 */ public class Student implements Serializable { private int id; private String name; private String sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } }
1.5.5、注意事项
<cache readOnly="false"/>
当我们的配置文件配置了cacheEnabled=true时,就会开启二级缓存,二级缓存是mapper级别的,也就说不同的sqlsession使用同一个mapper查询是,查询到的数据可能是另一个sqlsession做相同操作留下的缓存。
查询数据的顺序为:二级缓存 -> 一级缓存 -> 数据库
1.6、二级缓存测试
默认情况下一级缓存只在同一个会话中有效:
用例:
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话1,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器1 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); //打开一个会话2,不自动提交 SqlSession session2 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器2 StudentMapper mapper2 = session2.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student2 = mapper2.selectStudentById(1); System.out.println(student2); SqlSessionFactoryUtils.closeSession(session1); SqlSessionFactoryUtils.closeSession(session2); }
结果:
如果需要将范围扩大到同一个namespace中有效可以使用二级缓存:
用例:
/**缓存测试*/ @Test public void cacheTest(){ //打开一个会话1,不自动提交 SqlSession session1 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器1 StudentMapper mapper1 = session1.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student1 = mapper1.selectStudentById(1); System.out.println(student1); //必须手动提交,否则无效 session1.commit(); //打开一个会话2,不自动提交 SqlSession session2 = SqlSessionFactoryUtils.openSqlSession(false); //获得一个映射器2 StudentMapper mapper2 = session2.getMapper(StudentMapper.class); //查询单个对象通过编号 Student student2 = mapper2.selectStudentById(1); System.out.println(student2); SqlSessionFactoryUtils.closeSession(session1); SqlSessionFactoryUtils.closeSession(session2); }
结果:
如果不手动提交查询结果也不会缓存成功。
使用两个不同的SqlSession对象去执行相同查询条件的查询,第二次查询时不会再发送SQL语句,而是直接从缓存中取出数据
1.7、二级缓存小结
1. 映射语句文件中的所有select语句将会被缓存。
2. 映射语句文件中的所有insert,update和delete语句会刷新缓存。
3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
4. 缓存会根据指定的时间间隔来刷新。
5. 缓存会存储1024个对象
cache标签常用属性:
<cache eviction="FIFO" <!--回收策略为先进先出--> flushInterval="60000" <!--自动刷新时间60s--> size="512" <!--最多缓存512个引用对象--> readOnly="true"/> <!--只读-->
二、MyBatis-Generator代码生成
2.1、在Intellij IDEA创建maven项目
这里创建了一个Maven项目,未使用骨架。
2.2、添加依赖
在maven项目的pom.xml 添加mybatis-generator-maven-plugin 插件
<?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>com.zhangguo.mybatis06</groupId> <artifactId>MyBatis06</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--MySql数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- JUnit单元测试工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- mybatis-generator-core 反向生成java代码--> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.5</version> </dependency> </dependencies> <!--mybatis 代码生成插件--> <build> <finalName>MyBatis06</finalName> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build> </project>
2.3、配置生成参数
在maven项目下的src/main/resources 目录下建立名为 generatorConfig.xml的配置文件,作为mybatis-generator-maven-plugin 插件的执行目标:
<?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="db.properties"></properties> <!--指定特定数据库的jdbc驱动jar包的位置 --> <classPathEntry location="${mysql.driverLocation}"/> <context id="default" targetRuntime="MyBatis3"> <!-- optional,旨在创建class时,对注释进行控制 --> <commentGenerator> <property name="suppressDate" value="true" /> </commentGenerator> <!--jdbc的数据库连接 --> <jdbcConnection driverClass="${mysql.driver}" connectionURL="${mysql.url}" userId="${mysql.username}" password="${mysql.password}"> </jdbcConnection> <!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类 targetPackage 指定生成的model生成所在的包名 targetProject 指定在该项目下所在的路径 --> <javaModelGenerator targetPackage="com.zhangguo.mybatis06.entities" targetProject="src/main/java"> <!-- 是否对model添加 构造函数 --> <property name="constructorBased" value="true"/> <!-- 是否允许子包,即targetPackage.schemaName.tableName --> <property name="enableSubPackages" value="false"/> <!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 --> <property name="immutable" value="true"/> <!-- 给Model添加一个父类 --> <property name="rootClass" value="com.zhangguo.mybatis06.entities.BaseEntity"/> <!-- 是否对类CHAR类型的列的数据进行trim操作 --> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 --> <sqlMapGenerator targetPackage="com.zhangguo.mybatis06.mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码 type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象 type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象 type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 --> <javaClientGenerator targetPackage="com.zhangguo.mybatis06.dao" targetProject="src/main/java" type="MIXEDMAPPER"> <property name="enableSubPackages" value=""/> <!-- 定义Maper.java 源代码中的ByExample() 方法的可视性,可选的值有: public; private; protected; default 注意:如果 targetRuntime="MyBatis3",此参数被忽略 --> <property name="exampleMethodVisibility" value=""/> <!-- 方法名计数器 Important note: this property is ignored if the target runtime is MyBatis3. --> <property name="methodNameCalculator" value=""/> <!-- 为生成的接口添加父接口 --> <property name="rootInterface" value=""/> </javaClientGenerator> <table tableName="student" schema="nfmall"></table> <table tableName="category" schema="nfmall"></table> <table tableName="goods" schema="nfmall"></table> </context> </generatorConfiguration>
这里的属性资源文件与mybatis共用db.propities文件
##MySQL连接字符串 #驱动 mysql.driver=com.mysql.jdbc.Driver #地址 mysql.url=jdbc:mysql://127.0.0.1:3306/nfmall?useUnicode=true&characterEncoding=UTF-8 #用户名 mysql.username=root #密码 mysql.password=uchr@123 #驱动位置 mysql.driverLocation=E:\\NF\\Java\\JDBC\\mysql-connector-java-5.1.47\\mysql-connector-java-5.1.47.jar
参数配置文件一:
<?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> <!-- 本地数据库驱动程序jar包的全路径 --> <classPathEntry location=""/> <context id="context" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressAllComments" value="false"/> <property name="suppressDate" value="true"/> </commentGenerator> <!-- 数据库的相关配置 --> <jdbcConnection driverClass="" connectionURL="" userId="" password=""/> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- 实体类生成的位置 --> <javaModelGenerator targetPackage="目标包" targetProject="目标项目classpath"> <property name="enableSubPackages" value="false"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- *Mapper.xml 文件的位置 --> <sqlMapGenerator targetPackage="目标包" targetProject="目标项目classpath"> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- Mapper 接口文件的位置 --> <javaClientGenerator targetPackage="目标包" targetProject="目标项目classpath" type="XMLMAPPER"> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- 相关表的配置 --> <table tableName="表名" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/> </context> </generatorConfiguration>
参考配置文件二:
<?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="generator.properties"></properties> <!--指定特定数据库的jdbc驱动jar包的位置 --> <classPathEntry location="${jdbc.driverLocation}"/> <context id="default" targetRuntime="MyBatis3"> <!-- optional,旨在创建class时,对注释进行控制 --> <commentGenerator> <property name="suppressDate" value="true" /> </commentGenerator> <!--jdbc的数据库连接 --> <jdbcConnection driverClass="${jdbc.driverClass}" connectionURL="${jdbc.connectionURL}" userId="${jdbc.userId}" password="${jdbc.password}"> </jdbcConnection> <!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类 targetPackage 指定生成的model生成所在的包名 targetProject 指定在该项目下所在的路径 --> <javaModelGenerator targetPackage="org.louis.hometutor.po" targetProject="src/main/java"> <!-- 是否对model添加 构造函数 --> <property name="constructorBased" value="true"/> <!-- 是否允许子包,即targetPackage.schemaName.tableName --> <property name="enableSubPackages" value="false"/> <!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 --> <property name="immutable" value="true"/> <!-- 给Model添加一个父类 --> <property name="rootClass" value="com.foo.louis.Hello"/> <!-- 是否对类CHAR类型的列的数据进行trim操作 --> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 --> <sqlMapGenerator targetPackage="org.louis.hometutor.domain" targetProject="src/main/java"> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码 type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象 type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象 type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 --> <javaClientGenerator targetPackage="com.foo.tourist.dao" targetProject="src/main/java" type="MIXEDMAPPER"> <property name="enableSubPackages" value=""/> <!-- 定义Maper.java 源代码中的ByExample() 方法的可视性,可选的值有: public; private; protected; default 注意:如果 targetRuntime="MyBatis3",此参数被忽略 --> <property name="exampleMethodVisibility" value=""/> <!-- 方法名计数器 Important note: this property is ignored if the target runtime is MyBatis3. --> <property name="methodNameCalculator" value=""/> <!-- 为生成的接口添加父接口 --> <property name="rootInterface" value=""/> </javaClientGenerator> <table tableName="lession" schema="louis"> <!-- optional , only for mybatis3 runtime 自动生成的键值(identity,或者序列值) 如果指定此元素,MBG将会生成<selectKey>元素,然后将此元素插入到SQL Map的<insert> 元素之中 sqlStatement 的语句将会返回新的值 如果是一个自增主键的话,你可以使用预定义的语句,或者添加自定义的SQL语句. 预定义的值如下: Cloudscape This will translate to: VALUES IDENTITY_VAL_LOCAL() DB2: VALUES IDENTITY_VAL_LOCAL() DB2_MF: SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1 Derby: VALUES IDENTITY_VAL_LOCAL() HSQLDB: CALL IDENTITY() Informix: select dbinfo('sqlca.sqlerrd1') from systables where tabid=1 MySql: SELECT LAST_INSERT_ID() SqlServer: SELECT SCOPE_IDENTITY() SYBASE: SELECT @@IDENTITY JDBC: This will configure MBG to generate code for MyBatis3 suport of JDBC standard generated keys. This is a database independent method of obtaining the value from identity columns. identity: 自增主键 If true, then the column is flagged as an identity column and the generated <selectKey> element will be placed after the insert (for an identity column). If false, then the generated <selectKey> will be placed before the insert (typically for a sequence). --> <generatedKey column="" sqlStatement="" identity="" type=""/> <!-- optional. 列的命名规则: MBG使用 <columnRenamingRule> 元素在计算列名的对应 名称之前,先对列名进行重命名, 作用:一般需要对BUSI_CLIENT_NO 前的BUSI_进行过滤 支持正在表达式 searchString 表示要被换掉的字符串 replaceString 则是要换成的字符串,默认情况下为空字符串,可选 --> <columnRenamingRule searchString="" replaceString=""/> <!-- optional.告诉 MBG 忽略某一列 column,需要忽略的列 delimitedColumnName:true ,匹配column的值和数据库列的名称 大小写完全匹配,false 忽略大小写匹配 是否限定表的列名,即固定表列在Model中的名称 --> <ignoreColumn column="PLAN_ID" delimitedColumnName="true" /> <!--optional.覆盖MBG对Model 的生成规则 column: 数据库的列名 javaType: 对应的Java数据类型的完全限定名 在必要的时候可以覆盖由JavaTypeResolver计算得到的java数据类型. For some databases, this is necessary to handle "odd" database types (e.g. MySql's unsigned bigint type should be mapped to java.lang.Object). jdbcType:该列的JDBC数据类型(INTEGER, DECIMAL, NUMERIC, VARCHAR, etc.),该列可以覆盖由JavaTypeResolver计算得到的Jdbc类型, 对某些数据库而言,对于处理特定的JDBC 驱动癖好 很有必要(e.g. DB2's LONGVARCHAR type should be mapped to VARCHAR for iBATIS). typeHandler: --> <columnOverride column="" javaType="" jdbcType="" typeHandler="" delimitedColumnName="" /> </table> </context> </generatorConfiguration>
属性资源文件:
jdbc.driverLocation=E:\\NF\\Java\\JDBC\\mysql-connector-java-5.1.47\\mysql-connector-java-5.1.47.jar jdbc.driverClass=com.mysql.jdbc.Driver jdbc.connectionURL=jdbc:mysql://127.0.0.1:3306/nfmall?useUnicode=true&characterEncoding=UTF-8 jdbc.userId=root jdbc.password=uchr@123
这里使用了外置的配置文件generator.properties,可以将一下属性配置到properties文件之中,增加配置的灵活性:
项目目录如下:
2.4、执行生成
在Maven Projects中找到Plugins->mybatis-generator->mybatis-generator:generate
点击运行,然后不出意外的话,会在控制台输出:
[INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building MyBatis06 1.0-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- mybatis-generator-maven-plugin:1.3.2:generate (default-cli) @ MyBatis06 --- [INFO] Connecting to the Database [INFO] Introspecting table nfmall.student log4j:WARN No appenders could be found for logger (org.mybatis.generator.internal.db.DatabaseIntrospector). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. [INFO] Introspecting table nfmall.category [INFO] Introspecting table nfmall.goods [INFO] Generating Example class for table student [INFO] Generating Record class for table student [INFO] Generating Mapper Interface for table student [INFO] Generating SQL Map for table student [INFO] Generating Example class for table category [INFO] Generating Record class for table category [INFO] Generating Mapper Interface for table category [INFO] Generating SQL Map for table category [INFO] Generating Example class for table goods [INFO] Generating Record class for table goods [INFO] Generating Mapper Interface for table goods [INFO] Generating SQL Map for table goods [INFO] Saving file StudentMapper.xml [INFO] Saving file CategoryMapper.xml [INFO] Saving file GoodsMapper.xml [INFO] Saving file StudentExample.java [INFO] Saving file Student.java [INFO] Saving file StudentMapper.java [INFO] Saving file CategoryExample.java [INFO] Saving file Category.java [INFO] Saving file CategoryMapper.java [INFO] Saving file GoodsExample.java [INFO] Saving file Goods.java [INFO] Saving file GoodsMapper.java [WARNING] Root class com.zhangguo.mybatis06.entities.BaseEntity cannot be loaded, checking for member overrides is disabled for this class [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.845 s [INFO] Finished at: 2018-10-10T09:51:45+08:00 [INFO] Final Memory: 11M/162M [INFO] ------------------------------------------------------------------------
看到BUILD SUCCESS,则大功告成,如果有错误的话,由于添加了-e 选项,会把具体的详细错误信息打印出来的,根据错误信息修改即可。
生成结果:
2.5、使用生成的代码
1、MyBatis会话工具类
package com.zhangguo.mybatis06.utils; 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 java.io.IOException; import java.io.InputStream; /** * MyBatis会话工具类 * */ public class SqlSessionFactoryUtils { /**会话工厂*/ private static SqlSessionFactory factory; static { try { /*获得配置文件的文件流*/ InputStream inputStream=Resources.getResourceAsStream("mybatisCfg.xml"); //初始化工厂 factory=new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /** * 获得会话对象 * 指定是否自动提交 * */ public static SqlSession openSqlSession(boolean isAutoCommit){ return getFactory().openSession(isAutoCommit); } public static SqlSessionFactory getFactory() { return factory; } public static void setFactory(SqlSessionFactory factory) { SqlSessionFactoryUtils.factory = factory; } /** * 关闭会话 * */ public static void closeSession(SqlSession session){ if(session!=null){ session.close(); } } }
2、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> <!--导入db.properties文件中的所有key-value数据--> <!--外部引入的内容将覆盖内部定义的--> <properties resource="db.properties"> <!--定义一个名称为driver,值为com.mysql.jdbc.Driver的属性--> <property name="mysql.driver" value="com.mysql.jdbc.Driver"></property> </properties> <settings> <!--设置是否允许缓存--> <setting name="cacheEnabled" value="true"/> <!--设置日志输出的目标--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--别名--> <typeAliases> <!--定义单个别名,指定名称为student,对应的类型为com.zhangguo.mybatis02.entities.Student--> <!--<typeAlias type="com.zhangguo.mybatis02.entities.Student" alias="student"></typeAlias>--> <!--指定包名下所有的类被自动扫描并定义默认别名, mybatis会自动扫描包中的pojo类,自动定义别名,别名就是类名(首字母大写或小写都可以)--> <package name="com.zhangguo.mybatis03.entities"></package> </typeAliases> <!--注册自定义的类型处理器--> <typeHandlers> <!--<typeHandler handler="" javaType="" jdbcType=""></typeHandler>--> </typeHandlers> <!--环境配置,default为默认选择的环境--> <environments default="development"> <!--开发--> <environment id="development"> <!--事务管理--> <transactionManager type="JDBC"/> <!--连接池--> <dataSource type="POOLED"> <!--引用属性${mysql.driver}--> <property name="driver" value="${mysql.driver}"/> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> </dataSource> </environment> <!--运行--> <environment id="work"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/nfmall?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <!--根据路径注册一个基于XML的映射器--> <mapper resource="com/zhangguo/mybatis06/mapper/studentMapper.xml"/> </mappers> </configuration>
3、测试用例
package test; import com.zhangguo.mybatis06.dao.StudentMapper; import com.zhangguo.mybatis06.entities.Student; import com.zhangguo.mybatis06.entities.StudentExample; import com.zhangguo.mybatis06.utils.SqlSessionFactoryUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class StudentTest { /*** * List<Student> selectByExample(StudentExample example); */ @Test public void testSelectByExample(){ List<Student> entities = null; //打开一个会话 SqlSession session = SqlSessionFactoryUtils.openSqlSession(true); //获得一个映射器 StudentMapper mapper = session.getMapper(StudentMapper.class); StudentExample studentExample=new StudentExample(); //查询名字中含a studentExample.createCriteria().andNameLike("%a%"); //查询多个对象,指定参数 entities = mapper.selectByExample(studentExample); //关闭 SqlSessionFactoryUtils.closeSession(session); } }
测试结果:
三、MyBatis-GUI代码生成器mybatis-generator-gui
3.1、概要
源码地址:https://github.com/zouzg/mybatis-generator-gui
mybatis-generator-gui是基于mybatis generator开发一款界面工具, 本工具可以使你非常容易及快速生成Mybatis的Java POJO文件及数据库Mapping文件。
3.2、核心特性
- 按照界面步骤轻松生成代码,省去XML繁琐的学习与配置过程
- 保存数据库连接与Generator配置,每次代码生成轻松搞定
- 内置常用插件,比如分页插件
- 把数据库中表列的注释生成为Java实体的注释,生成的实体清晰明了
- 可选的去除掉对版本管理不友好的注释,这样新增或删除字段重新生成的文件比较过来清楚
- 目前已经支持Mysql、Mysql8、Oracle、PostgreSQL与SQL Server,暂不对其他非主流数据库提供支持。
3.3、要求
本工具由于使用了Java 8的众多特性,所以要求JDK 1.8.0.60以上版本,另外JDK 1.9暂时还不支持。
3.4、下载
你可以从本链接下载本工具: https://github.com/astarring/mybatis-generator-gui/releases
3.5、启动本软件
- 方法一: 自助构建(注意项目名称需要根据实例情况修改)
git clone https://github.com/astarring/mybatis-generator-gui cd mybatis-generator-gui mvn jfx:jar cd target/jfx/app/ java -jar mybatis-generator-gui.jar
- 方法二: IDE中运行
Eclipse or IntelliJ IDEA中启动, 找到com.zzg.mybatis.generator.MainUI
类并运行就可以了
-
方法三:打包为本地原生应用,双击快捷方式即可启动,方便快捷
如果不想打包后的安装包logo为Java的灰色的茶杯,需要在pom文件里将对应操作系统平台的图标注释放开
#<icon>${project.basedir}/package/windows/mybatis-generator-gui.ico</icon>为windows #<icon>${project.basedir}/package/macosx/mybatis-generator-gui.icns</icon>为mac mvn jfx:native
另外需要注意,windows系统打包成exe的话需要安装WiXToolset3+的环境;由于打包后会把jre打入安装包,两个平台均100M左右,体积较大请自行打包;打包后的安装包在target/jfx/native目录下
3.6、注意事项
- 本自动生成代码工具只适合生成单表的增删改查,对于需要做数据库联合查询的,请自行写新的XML与Mapper;
- 部分系统在中文输入方法时输入框中无法输入文字,请切换成英文输入法;
- 如果不明白对应字段或选项是什么意思的时候,把光标放在对应字段或Label上停留一会然后如果有解释会出现解释;
3.7、文档
更多详细文档请参考本库的Wiki
3.8、代码生成示例
3.8.1、创建一个Maven项目
3.8.2、下载源代码并使用IDEA打开
下载地址:https://github.com/zouzg/mybatis-generator-gui/releases
解压后引入到IDEA中
找到MainUI类
3.8.3、运行程序
3.8.4、连接到数据
3.8.5、生成代码
3.8.6、使用生成的代码
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>com.zhangguo.mybatis05</groupId> <artifactId>MyBatis05</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--MySql数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- JUnit单元测试工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> </project>
工具类:
package com.zhangguo.mybatis05.utils; 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 java.io.IOException; import java.io.InputStream; /** * MyBatis会话工具类 * */ public class SqlSessionFactoryUtils { /**会话工厂*/ private static SqlSessionFactory factory; static { try { /*获得配置文件的文件流*/ InputStream inputStream=Resources.getResourceAsStream("mybatisCfg.xml"); //初始化工厂 factory=new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } /** * 获得会话对象 * 指定是否自动提交 * */ public static SqlSession openSqlSession(boolean isAutoCommit){ return getFactory().openSession(isAutoCommit); } public static SqlSessionFactory getFactory() { return factory; } public static void setFactory(SqlSessionFactory factory) { SqlSessionFactoryUtils.factory = factory; } /** * 关闭会话 * */ public static void closeSession(SqlSession session){ if(session!=null){ session.close(); } } }
核心配置文件:
<?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> <!--导入db.properties文件中的所有key-value数据--> <!--外部引入的内容将覆盖内部定义的--> <properties resource="db.properties"> <!--定义一个名称为driver,值为com.mysql.jdbc.Driver的属性--> <property name="mysql.driver" value="com.mysql.jdbc.Driver"></property> </properties> <settings> <!--设置是否允许缓存--> <setting name="cacheEnabled" value="true"/> <!--设置日志输出的目标--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--别名--> <typeAliases> <!--定义单个别名,指定名称为student,对应的类型为com.zhangguo.mybatis02.entities.Student--> <!--<typeAlias type="com.zhangguo.mybatis02.entities.Student" alias="student"></typeAlias>--> <!--指定包名下所有的类被自动扫描并定义默认别名, mybatis会自动扫描包中的pojo类,自动定义别名,别名就是类名(首字母大写或小写都可以)--> <package name="com.zhangguo.mybatis03.entities"></package> </typeAliases> <!--注册自定义的类型处理器--> <typeHandlers> <!--<typeHandler handler="" javaType="" jdbcType=""></typeHandler>--> </typeHandlers> <!--环境配置,default为默认选择的环境--> <environments default="development"> <!--开发--> <environment id="development"> <!--事务管理--> <transactionManager type="JDBC"/> <!--连接池--> <dataSource type="POOLED"> <!--引用属性${mysql.driver}--> <property name="driver" value="${mysql.driver}"/> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> </dataSource> </environment> <!--运行--> <environment id="work"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/nfmall?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <!--根据路径注册一个基于XML的映射器--> <mapper resource="com/zhangguo/mybatis05/StudentMapper.xml"/> </mappers> </configuration>
POJO:
package com.zhangguo.mybatis05.entities; import java.io.Serializable; /** * @author */ public class Student implements Serializable { private Integer id; private String name; private String sex; private static final long serialVersionUID = 1L; 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public boolean equals(Object that) { if (this == that) { return true; } if (that == null) { return false; } if (getClass() != that.getClass()) { return false; } Student other = (Student) that; return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName())) && (this.getSex() == null ? other.getSex() == null : this.getSex().equals(other.getSex())); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); result = prime * result + ((getSex() == null) ? 0 : getSex().hashCode()); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", name=").append(name); sb.append(", sex=").append(sex); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } }
参数:
package com.zhangguo.mybatis05.entities; import java.util.ArrayList; import java.util.List; public class StudentExample { protected String orderByClause; protected boolean distinct; protected List<Criteria> oredCriteria; private Integer limit; private Integer offset; public StudentExample() { oredCriteria = new ArrayList<Criteria>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List<Criteria> getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } public void setLimit(Integer limit) { this.limit = limit; } public Integer getLimit() { return limit; } public void setOffset(Integer offset) { this.offset = offset; } public Integer getOffset() { return offset; } protected abstract static class GeneratedCriteria { protected List<Criterion> criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<Criterion>(); } public boolean isValid() { return criteria.size() > 0; } public List<Criterion> getAllCriteria() { return criteria; } public List<Criterion> getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Integer value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Integer value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Integer value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Integer value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Integer value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Integer value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List<Integer> values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List<Integer> values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Integer value1, Integer value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Integer value1, Integer value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List<String> values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List<String> values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andSexIsNull() { addCriterion("sex is null"); return (Criteria) this; } public Criteria andSexIsNotNull() { addCriterion("sex is not null"); return (Criteria) this; } public Criteria andSexEqualTo(String value) { addCriterion("sex =", value, "sex"); return (Criteria) this; } public Criteria andSexNotEqualTo(String value) { addCriterion("sex <>", value, "sex"); return (Criteria) this; } public Criteria andSexGreaterThan(String value) { addCriterion("sex >", value, "sex"); return (Criteria) this; } public Criteria andSexGreaterThanOrEqualTo(String value) { addCriterion("sex >=", value, "sex"); return (Criteria) this; } public Criteria andSexLessThan(String value) { addCriterion("sex <", value, "sex"); return (Criteria) this; } public Criteria andSexLessThanOrEqualTo(String value) { addCriterion("sex <=", value, "sex"); return (Criteria) this; } public Criteria andSexLike(String value) { addCriterion("sex like", value, "sex"); return (Criteria) this; } public Criteria andSexNotLike(String value) { addCriterion("sex not like", value, "sex"); return (Criteria) this; } public Criteria andSexIn(List<String> values) { addCriterion("sex in", values, "sex"); return (Criteria) this; } public Criteria andSexNotIn(List<String> values) { addCriterion("sex not in", values, "sex"); return (Criteria) this; } public Criteria andSexBetween(String value1, String value2) { addCriterion("sex between", value1, value2, "sex"); return (Criteria) this; } public Criteria andSexNotBetween(String value1, String value2) { addCriterion("sex not between", value1, value2, "sex"); return (Criteria) this; } } /** */ public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List<?>) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } }
映射器:
<?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.zhangguo.mybatis05.mapper.StudentMapper"> <resultMap id="BaseResultMap" type="com.zhangguo.mybatis05.entities.Student"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="sex" jdbcType="CHAR" property="sex" /> </resultMap> <sql id="Example_Where_Clause"> <where> <foreach collection="oredCriteria" item="criteria" separator="or"> <if test="criteria.valid"> <trim prefix="(" prefixOverrides="and" suffix=")"> <foreach collection="criteria.criteria" item="criterion"> <choose> <when test="criterion.noValue"> and ${criterion.condition} </when> <when test="criterion.singleValue"> and ${criterion.condition} #{criterion.value} </when> <when test="criterion.betweenValue"> and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} </when> <when test="criterion.listValue"> and ${criterion.condition} <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> #{listItem} </foreach> </when> </choose> </foreach> </trim> </if> </foreach> </where> </sql> <sql id="Update_By_Example_Where_Clause"> <where> <foreach collection="example.oredCriteria" item="criteria" separator="or"> <if test="criteria.valid"> <trim prefix="(" prefixOverrides="and" suffix=")"> <foreach collection="criteria.criteria" item="criterion"> <choose> <when test="criterion.noValue"> and ${criterion.condition} </when> <when test="criterion.singleValue"> and ${criterion.condition} #{criterion.value} </when> <when test="criterion.betweenValue"> and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} </when> <when test="criterion.listValue"> and ${criterion.condition} <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> #{listItem} </foreach> </when> </choose> </foreach> </trim> </if> </foreach> </where> </sql> <sql id="Base_Column_List"> id, name, sex </sql> <select id="selectByExample" parameterType="com.zhangguo.mybatis05.entities.StudentExample" resultMap="BaseResultMap"> select <if test="distinct"> distinct </if> <include refid="Base_Column_List" /> from student <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> <if test="orderByClause != null"> order by ${orderByClause} </if> <if test="limit != null"> <if test="offset != null"> limit ${offset}, ${limit} </if> <if test="offset == null"> limit ${limit} </if> </if> </select> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from student where id = #{id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from student where id = #{id,jdbcType=INTEGER} </delete> <delete id="deleteByExample" parameterType="com.zhangguo.mybatis05.entities.StudentExample"> delete from student <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> </delete> <insert id="insert" parameterType="com.zhangguo.mybatis05.entities.Student"> insert into student (id, name, sex) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{sex,jdbcType=CHAR}) </insert> <insert id="insertSelective" parameterType="com.zhangguo.mybatis05.entities.Student"> insert into student <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="name != null"> name, </if> <if test="sex != null"> sex, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="sex != null"> #{sex,jdbcType=CHAR}, </if> </trim> </insert> <select id="countByExample" parameterType="com.zhangguo.mybatis05.entities.StudentExample" resultType="java.lang.Long"> select count(*) from student <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> </select> <update id="updateByExampleSelective" parameterType="map"> update student <set> <if test="record.id != null"> id = #{record.id,jdbcType=INTEGER}, </if> <if test="record.name != null"> name = #{record.name,jdbcType=VARCHAR}, </if> <if test="record.sex != null"> sex = #{record.sex,jdbcType=CHAR}, </if> </set> <if test="_parameter != null"> <include refid="Update_By_Example_Where_Clause" /> </if> </update> <update id="updateByExample" parameterType="map"> update student set id = #{record.id,jdbcType=INTEGER}, name = #{record.name,jdbcType=VARCHAR}, sex = #{record.sex,jdbcType=CHAR} <if test="_parameter != null"> <include refid="Update_By_Example_Where_Clause" /> </if> </update> <update id="updateByPrimaryKeySelective" parameterType="com.zhangguo.mybatis05.entities.Student"> update student <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> <if test="sex != null"> sex = #{sex,jdbcType=CHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.zhangguo.mybatis05.entities.Student"> update student set name = #{name,jdbcType=VARCHAR}, sex = #{sex,jdbcType=CHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper>
接口:
package com.zhangguo.mybatis05.mapper; import com.zhangguo.mybatis05.entities.Student; import com.zhangguo.mybatis05.entities.StudentExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface StudentMapper { long countByExample(StudentExample example); int deleteByExample(StudentExample example); int deleteByPrimaryKey(Integer id); int insert(Student record); int insertSelective(Student record); List<Student> selectByExample(StudentExample example); Student selectByPrimaryKey(Integer id); int updateByExampleSelective(@Param("record") Student record, @Param("example") StudentExample example); int updateByExample(@Param("record") Student record, @Param("example") StudentExample example); int updateByPrimaryKeySelective(Student record); int updateByPrimaryKey(Student record); }
数据访问类:
package com.zhangguo.mybatis05.dao; import com.zhangguo.mybatis05.entities.Student; import com.zhangguo.mybatis05.entities.StudentExample; import com.zhangguo.mybatis05.utils.SqlSessionFactoryUtils; import com.zhangguo.mybatis05.mapper.StudentMapper; import org.apache.ibatis.session.SqlSession; import java.util.List; import java.util.Map; public class StudentDao implements StudentMapper { public long countByExample(StudentExample example) { return 0; } public int deleteByExample(StudentExample example) { return 0; } public int deleteByPrimaryKey(Integer id) { return 0; } public int insert(Student record) { return 0; } public int insertSelective(Student record) { return 0; } public List<Student> selectByExample(StudentExample example) { List<Student> entities = null; //打开一个会话 SqlSession session = SqlSessionFactoryUtils.openSqlSession(true); //获得一个映射器 StudentMapper mapper = session.getMapper(StudentMapper.class); //查询多个对象 entities = mapper.selectByExample(example); //关闭 SqlSessionFactoryUtils.closeSession(session); return entities; } public Student selectByPrimaryKey(Integer id) { return null; } public int updateByExampleSelective(Student record, StudentExample example) { return 0; } public int updateByExample(Student record, StudentExample example) { return 0; } public int updateByPrimaryKeySelective(Student record) { return 0; } public int updateByPrimaryKey(Student record) { return 0; } }
单元测试:
package com.zhangguo.mybatis05.dao; import com.zhangguo.mybatis05.entities.Student; import com.zhangguo.mybatis05.entities.StudentExample; import org.junit.Test; import org.junit.Before; import org.junit.After; import java.util.List; /** * StudentDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>10/09/2018</pre> */ public class StudentDaoTest { StudentDao dao; @Before public void before() throws Exception { dao = new StudentDao(); } @After public void after() throws Exception { } /** * Method: countByExample(StudentExample example) */ @Test public void testCountByExample() throws Exception { //TODO: Test goes here... } /** * Method: deleteByExample(StudentExample example) */ @Test public void testDeleteByExample() throws Exception { //TODO: Test goes here... } /** * Method: deleteByPrimaryKey(Integer id) */ @Test public void testDeleteByPrimaryKey() throws Exception { //TODO: Test goes here... } /** * Method: insert(Student record) */ @Test public void testInsert() throws Exception { //TODO: Test goes here... } /** * Method: insertSelective(Student record) */ @Test public void testInsertSelective() throws Exception { //TODO: Test goes here... } /** * Method: selectByExample(StudentExample example) */ @Test public void testSelectByExample() throws Exception { StudentExample se = new StudentExample(); se.createCriteria().andIdBetween(3, 5); List<Student> students = dao.selectByExample(se); System.out.println(students); } /** * Method: selectByPrimaryKey(Integer id) */ @Test public void testSelectByPrimaryKey() throws Exception { //TODO: Test goes here... } /** * Method: updateByExampleSelective(Student record, StudentExample example) */ @Test public void testUpdateByExampleSelective() throws Exception { //TODO: Test goes here... } /** * Method: updateByExample(Student record, StudentExample example) */ @Test public void testUpdateByExample() throws Exception { //TODO: Test goes here... } /** * Method: updateByPrimaryKeySelective(Student record) */ @Test public void testUpdateByPrimaryKeySelective() throws Exception { //TODO: Test goes here... } /** * Method: updateByPrimaryKey(Student record) */ @Test public void testUpdateByPrimaryKey() throws Exception { //TODO: Test goes here... } }
运行结果:
其它GUI工具:
https://github.com/spawpaw/mybatis-generator-gui-extension
mybatis-generator-gui-extension是一个为MybatisGenerator编写的图形化界面,为实体/Example/Mapper提供了丰富的扩展。
https://github.com/xialeistudio/mybatis-generator-gui
可视化mybatis生成工具
[https://www.oschina.net/p/mybatis-generator-gui]
四、示例源代码
https://git.dev.tencent.com/zhangguo5/MyBatis06.git
https://git.coding.net/zhangguo5/MyBatis03.git
https://git.coding.net/zhangguo5/MyBatis02.git
五、视频
https://www.bilibili.com/video/av32447485/
六、大作业
概述:
在中国云南有一个美丽的地方叫瑞丽,那里盛产玉饰,为了发展网络经济打开销路,有一家叫瑞丽玉源的公司搭建了一个电子商务网站,要求网站中可以按多种条件对商品进行搜索,可以同时按照多个条件进行过滤,包括:品牌、价格、颜色、水种、镶嵌、寓意和挂件类型。
功能需求:
1、要展示的数据已给出,在素材与数据\Data文件夹下有MSSQL Server数据库SouthMall、SQL脚本与Excel格式的数据,使用其中任意方式可以获得需要的数据。
2、数据库中有一个名为Products的商品表,表结构如表3-1所示:
序号 |
列名 |
说明 |
数据类型 |
长度 |
小数位 |
标识 |
主键 |
允许空 |
1 |
Id |
编号 |
int |
4 |
0 |
|
是 |
否 |
2 |
Name |
商品名称 |
nvarchar |
255 |
0 |
|
|
是 |
3 |
Color |
颜色 |
nvarchar |
500 |
0 |
|
|
是 |
4 |
BrandId |
品牌 |
int |
4 |
0 |
|
|
是 |
5 |
InLayId |
镶嵌 |
Int |
4 |
0 |
|
|
是 |
6 |
MoralId |
寓意 |
Int |
4 |
0 |
|
|
是 |
7 |
IceTypeId |
种地 |
Int |
4 |
0 |
|
|
是 |
8 |
HangTypeId |
挂件类型 |
int |
4 |
0 |
|
|
是 |
9 |
MarketPrice |
市场价格 |
float |
8 |
0 |
|
|
是 |
10 |
MyPrice |
商城价格 |
float |
8 |
0 |
|
|
是 |
11 |
Picture |
图片 |
nvarchar |
255 |
0 |
|
|
是 |
表3-1
请将Color、Brand、InLay、Moral、IceType、HangType作成外键,拆分成7张表
3、默认状态为所有条件都选择全部,如图3-1所示,即展示所有商品;
图 3-1
4、当用户点击不同的条件时可以进行多种组合过滤出相应结果,被选择条件项应该高亮显示,而这时不应该勾选全部(这里使用了两张图片,一张是选择全部checked.jpg,一张是未选择全部unchecked.jpg,存放在素材与数据/UI图片目录中)如图3-2所示;
图3-2
技术实现:
1、为了考虑用户体验变换条件时不能刷新整页,可考虑使用AJAX。jQuery库文件已存放在素材与数据/Scripts目录下,请不要使用ASP.NET AJAX技术(微软封装的服务器AJAX技术)。
2、搜索条件应该根据数据库动态生成,价格除外。
3、如果使用ASP.NET WebForms技术实现,页面中不能出现ViewState(页面状态),请禁用控件与页面的状态(EnableViewState="false")。
4、可以使用任意B/S技术,如.NET,Java或PHP平台下的Web技术。
5、除指定要求外,不对UI作特殊要求,如色彩、布局与字体等,基本规整就可以。UI用到的所有素材已保存在素材与数据文件夹下。
6、数据访问方式不作限制,可以使用任意ORM框架或数据库访问组件,如JDB、MyBatis、Hibernate、LINQ to SQL/Entity、Entity Framework、ADO.NET等。
普通:
分页
后台添加
模块块、Maven、Git
高级:
在普通的基础上增加多字段排序(可以选择排序类型与升降序)
CRUD
素材下载:https://git.dev.tencent.com/zhangguo5/MyBatis06.git