Mybatis Generator (MBG)
概念
MyBatis Generator:MyBatis 的开发团队提供了一个很强大的代码生成器, 代码包含了数据库表对应的实体 类 ,Mapper 接口类, Mapper xml 文件和 Example 对象等, 这些代码文件中几乎包含了全部的单表操作方 法, 使用 MBG 可以极大程度上方便我们使用 MyBatis, 还可以减少很多重复操作;
配置
generatorConfiguration - 根节点
properties - 用于指定一个需要在配置中解析使用的外部属性文件;
classPathEntry - 在 MBG 工作的时候, 需要额外加载的依赖包;
context - 用于指定生成一组对象的环境
property (0 个或多个) - 设置一些固定属性
plugin (0 个或多个)- 定义一个插件, 用于扩展或修改通过 MBG 生成的代码
commentGenerator (0 个或 1 个) - 该标签用来配置如何生成注释信息
jdbcConnection ( 1 个)- 必须要有的, 使用这个配置链接数据库
javaTypeResolver ( 0 个或 1 个) - 指定 JDBC 类型和 Java 类型如何转换
javaModelGenerator ( 1 个) - java 模型创建器
sqlMapGenerator (0 个或 1 个)- 生成 SQL map 的 xml 文件生成器
javaClientGenerator (0 个或 1 个)- 生成 Mapper 接口
table ( 1 个或多个) - 选择一个 table 来生成相关文件, 可以有一个或多个 table
示例:
- 1,db.properties
- jdbc_driver=com.MySQL.jdbc.Driver
- jdbc_url=jdbc:MySQL://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
- jdbc_username=root
- jdbc_password=lixun033
- project_src =src/main/java
- project_mapper_xml =src/main/resources/sqlmapper
- class_path=D:/devTools/LocalRepository/MySQL/MySQL-connector-java/5.1.18/MySQL-connector-java-5.1.18.jar
- 2,generatorConfig.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="db.properties" />
- <!-- 加载数据库驱动 -->
- <classPathEntry location="${class_path}" />
- <!-- context: 生成一组对象的环境
- id: 必选, 上下文 id, 用于在生成错误时提示
- defaultModelType: 指定生成对象的样式
- 1,conditional: 类似 hierarchical;
- 2,flat: 所有内容 (主键, blob) 等全部生成在一个对象中, 推荐使用;
- 3,hierarchical: 主键生成一个 XXKey 对象(key class),Blob 等单独生成一个对象, 其他简单属性在一个对象中(record class)
- targetRuntime:
- 1,MyBatis3: 默认的值, 生成基于 MyBatis3.x 以上版本的内容, 包括 XXXBySample;
- 2,MyBatis3Simple: 类似 MyBatis3, 只是不生成 XXXBySample;
- -->
- <context id="context1" targetRuntime="MyBatis3Simple" defaultModelType="flat">
- <!-- 生成的 Java 文件的编码 -->
- <property name="javaFileEncoding" value="UTF-8"/>
- <commentGenerator>
- <!-- 是否去除自动生成的注释 true: 是 : false: 否 -->
- <property name="suppressAllComments" value="false" />
- <!-- 阻止注释中包含时间戳 true: 是 : false: 否 -->
- <property name="suppressDate" value="true" />
- <!-- 注释是否包含数据库表的注释信息 true: 是 : false: 否 -->
- <property name="addRemarkComments" value="true" />
- </commentGenerator>
- <!-- 数据库连接的信息: 驱动类, 连接地址, 用户名, 密码 -->
- <jdbcConnection driverClass="${jdbc_driver}"
- connectionURL="${jdbc_url}" userId="${jdbc_username}" password="${jdbc_password}" />
- <!-- java 模型创建器, 是必须要的元素 负责: 1,key 类(见 context 的 defaultModelType);2,java 类; 3, 查询类
- targetPackage: 生成的类要放的包, 真实的包受 enableSubPackages 属性控制;
- targetProject: 目标项目, 指定一个存在的目录下, 生成的内容会放到指定目录中, 如果目录不存在, MBG 不会自动建目录
- -->
- <javaModelGenerator targetPackage="com.enjoylearning.mybatis.entity" targetProject="${project_src}">
- <!-- 设置一个根对象,
- 如果设置了这个根对象, 那么生成的 keyClass 或者 recordClass 会继承这个类; 在 Table 的 rootClass 属性中可以覆盖该选项
- 注意: 如果在 key class 或者 record class 中有 root class 相同的属性, MBG 就不会重新生成这些属性了, 包括:
- 1, 属性名相同, 类型相同, 有相同的 getter/setter 方法;
- -->
- <property name="rootClass" value="com.enjoylearning.mybatis.entity.BaseEntity" />
- </javaModelGenerator>
- <!-- 生成 SQL map 的 XML 文件生成器,
- targetPackage: 生成的类要放的包, 真实的包受 enableSubPackages 属性控制;
- targetProject: 目标项目, 指定一个存在的目录下, 生成的内容会放到指定目录中, 如果目录不存在, MBG 不会自动建目录
- -->
- <sqlMapGenerator targetPackage="." targetProject="${project_mapper_xml}">
- </sqlMapGenerator>
- <!-- 对于 mybatis 来说, 即生成 Mapper 接口, 注意, 如果没有配置该元素, 那么默认不会生成 Mapper 接口
- type: 选择怎么生成 mapper 接口(在 MyBatis3/MyBatis3Simple 下):
- 1,ANNOTATEDMAPPER: 会生成使用 Mapper 接口 + Annotation 的方式创建(SQL 生成在 annotation 中), 不会生成对应的 XML;
- 2,MIXEDMAPPER: 使用混合配置, 会生成 Mapper 接口, 并适当添加合适的 Annotation, 但是 XML 会生成在 XML 中;
- 3,XMLMAPPER: 会生成 Mapper 接口, 接口完全依赖 XML;
- 注意, 如果 context 是 MyBatis3Simple: 只支持 ANNOTATEDMAPPER 和 XMLMAPPER
- -->
- <javaClientGenerator targetPackage="com.enjoylearning.mybatis.mapper" targetProject="${project_src}" type="XMLMAPPER" />
- <!-- shema 数据库 tableName 表明 -->
- <table schema="${jdbc_username}" tableName="%">
- <generatedKey column="id" sqlStatement="MySql"/>
- </table>
- </context>
- </generatorConfiguration>
如何运行 -----1,2 适用于对逆向工程定制较多, 项目工程结构比较单一的情况
1,Maven Plugin----- 直接通过 maven 命令生成即可
mvn mybatis-generator:generate
2, 通过 java 程序, xml 配置文件的方式 -----generatorConfig.xml 为上面的配置文件
- @Test
- public void mybatisGeneratorTest() throws FileNotFoundException{
- List<String> warnings = new ArrayList<String>();
- boolean overwrite = true;
- String genCfg = "generatorConfig.xml";
- File configFile = new File(getClass().getClassLoader().getResource(genCfg).getFile());
- ConfigurationParser cp = new ConfigurationParser(warnings);
- Configuration config = null;
- try {
- config = cp.parseConfiguration(configFile);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (XMLParserException e) {
- e.printStackTrace();
- }
- DefaultShellCallback callback = new DefaultShellCallback(overwrite);
- MyBatisGenerator myBatisGenerator = null;
- try {
- myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
- } catch (InvalidConfigurationException e) {
- e.printStackTrace();
- }
- try {
- myBatisGenerator.generate(null);
- } catch (SQLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
3, 命令提示符, xml 配置文件
java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml
其实原理都是一样的, 这地方需要注意数据库驱动的位置.
Example
比如有个 TUser 的实体类, 在 targetRuntime="MyBatis3" 时, 会生成一个 TUserExample 的类, 这个类里面包含了很多种查询场景, 简化开发, 不过不建议使用, sql 调优会受到很大的影响.
<context id="context1" targetRuntime="MyBatis3" defaultModelType="flat">
关联查询
在关系型数据库中, 我们经常要处理一对一 , 一对多的关系 . 例如, 一辆汽车需要有一个引擎, 这是一对一的 关系. 一辆汽车有 4 个或更多个轮子, 这是一对多的关系 . 关联元素就是专门用来处理关联关系的;
一对一 嵌套结果 ----- 精髓就是一次查询出所有结果, 然后将结果拼成结构化样式
association 标签 嵌套结果方式 常用属性:
property : 对应实体类中的属性名, 必填项.
javaType : 属性对应的 Java 类型 .
resultMap : 可以直接使用现有的 resultMap , 而不需要在这里配置映射关系.
columnPrefix : 查询列的前缀, 配置前缀后, 在子标签配置 result 的 column 时可以省略前缀
Tips:
1. resultMap 可以通过使用 extends 实现继承关系, 简化很多配置工作量;
2. 通过添加完整的命名空间, 可以引用其他 xml 文件的 resultMap
- <select id="selectUserPosition1" resultMap="userAndPosition1">
- select user_name,
- real_name,
- sex,
- mobile,
- email,
- a.note,
- b.id post_id,
- b.post_name,
- b.note post_note
- from t_user a,
- t_position b
- where a.position_id = b.id
- </select>
- <resultMap id="userAndPosition1" extends="BaseResultMap" type="TUser">
- <association property="position" javaType="com.enjoylearning.mybatis.entity.TPosition" columnPrefix="post_">
- <id column="id" property="id"/>
- <result column="name" property="postName"/>
- <result column="note" property="note"/>
- </association>
- </resultMap>
一对一 嵌套查询
association 标签 嵌套查询方式 常用属性:
select : 另 一个映射查询的 id, MyBatis 会额外执行这个查询获取嵌套对象的结果 .
column : 列名(或别名), 将主查询中列的结果作为嵌套查询的 参数, 配置方式如 column={propl=coll , prop2=col2}, propl 和 prop2 将作为嵌套查询的参数.
fetchType : 数据加载方式, 可选值为 lazy 和 eager, 分别为延迟加载和积极加载 , 这个配置会覆盖全局的 lazyLoadingEnabled 配置;
Tips:"N+1 查询问题" 使用 "fetchType=lazy" 并且全局 setting 进行改善.----- 比如有 teacher 以及 student 表. 首先根据 teacherId 查询 teacher 数据, 一个 teacher 对应多个 student. 从 teacher 表中通过 studentId 查询 student 信息, 如果代码中暂时不需要 student 的信息, 那么暂时不会查询 student 表数据.
<setting name="aggressiveLazyLoading" value="false"/>
概括地讲, N+1 查询问题可以是这样引起的:
你执行了一个单独的 SQL 语句来获取结果列表(就是 "+1").
对返回的每条记录, 你执行了一个查询语句来为每个加载细节(就是 "N").
这个问题会导致成百上千的 SQL 语句被执行. 这通常不是期望的.
- <select id="selectUserPosition2" resultMap="userAndPosition2">
- select
- a.id,
- a.user_name,
- a.real_name,
- a.sex,
- a.mobile,
- a.position_id
- from t_user a
- </select>
- <resultMap id="userAndPosition2" extends="BaseResultMap" type="TUser">
- <association property="position" column="position_id" select="com.enjoylearning.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />
- </resultMap>
一对多 嵌套结果
1. collection 支持的属性以及属性的作用和 association 完全相同
2. mybatis 会根据 id 标签, 进行字段的合并, 合理配置好 ID 标签可以提高处理的效率;
Tips:
如果要配置一个相当复杂的映射, 一定要从基础映射开始配置, 每增加一些配置就进行对应的测试, 在循序渐进的过程中更容易发现和解决问题 .
- <select id="selectUserJobs1" resultMap="userAndJobs1">
- select
- a.id,
- a.user_name,
- a.real_name,
- a.sex,
- a.mobile,
- b.comp_name,
- b.years,
- b.title
- from t_user a,
- t_job_history b
- where a.id = b.user_id
- </select>
- <resultMap id="userAndJobs1" extends="BaseResultMap" type="TUser">
- <collection property="jobs" ofType="com.enjoylearning.mybatis.entity.TJobHistory">
- <result column="comp_name" property="compName" jdbcType="VARCHAR" />
- <result column="years" property="years" jdbcType="INTEGER" />
- <result column="title" property="title" jdbcType="VARCHAR" />
- </collection>
- </resultMap>
一对多 嵌套查询
- <select id="selectUserJobs2" resultMap="userAndJobs2">
- select
- a.id,
- a.user_name,
- a.real_name,
- a.sex,
- a.mobile
- from t_user a
- </select>
- <resultMap id="userAndJobs2" extends="BaseResultMap" type="TUser">
- <collection property="jobs" fetchType="lazy" column="id"
- select="com.enjoylearning.mybatis.mapper.TJobHistoryMapper.selectByUserId" />
- </resultMap>
discriminator 鉴别器映射
在特定的情况下使用不同的 pojo 进行关联, 鉴别器元素就是被设计来处理这个情况的. 鉴别器非常容易理解, 因为它 的表现很像 Java 语言中的 switch 语句;
discriminator 标签常用的两个属性如下:
column: 该属性用于设置要进行鉴别比较值的列 .
javaType: 该属性用于指定列的类型, 保证使用相同的 Java 类型来比较值.
discriminator 标签可以有 1 个或多个 case 标签, case 标签包含以下三个属性 .
value : 该值为 discriminator 指定 column 用来匹配的值 .
resultMap : 当 column 的值和 value 的值匹配时, 可以配置使用 resultMap 指定的映射, resultMap 优先级 高于 resultType .
resultType : 当 column 的值和 value 的值匹配时, 用于配置使用 resultType 指定的映射.
示例: 根据性别区分男, 女体检报告
- <sql id="Base_Column_List">
- id, user_name, real_name, sex, mobile, email, note,
- position_id
- </sql>
- <select id="selectUserHealthReport" resultMap="userAndHealthReport">
- select
- <include refid="Base_Column_List" />
- from t_user a
- </select>
- <resultMap id="userAndHealthReport" extends="BaseResultMap" type="TUser">
- <discriminator column="sex" javaType="int">
- <case value="1" resultMap="userAndHealthReportMale"/>
- <case value="2" resultMap="userAndHealthReportFemale"/>
- </discriminator>
- </resultMap>
- <resultMap id="userAndHealthReportMale" extends="userAndHealthReport" type="TUser">
- <collection property="healthReports" column="id" select= "com.enjoylearning.mybatis.mapper.THealthReportMaleMapper.selectByUserId"></collection>
- </resultMap>
- <resultMap id="userAndHealthReportFemale" extends="userAndHealthReport" type="TUser">
- <collection property="healthReports" column="id" select= "com.enjoylearning.mybatis.mapper.THealthReportFemaleMapper.selectByUserId"></collection>
- </resultMap>
缓存
一级缓存 ----- 默认开启
MyBatis 包含一个非常强大的查询缓存特性, 使用缓存可以使应用更快地获取数据, 避免频繁的数据库交互 ;
一级缓存 (也叫应用缓存):
一级缓存默认会启用, 想要关闭一级缓存可以在 select 标签上配置 flushCache="true";
一级缓存存在于 SqlSession 的生命周期中, 在同一个 SqlSession 中查询时, MyBatis 会把执行的方法和 参数通过算法生成缓存的键值, 将键值和查询结果存入一个 Map 对象中. 如果同一个 SqlSession 中执行的 方法和参数完全一致, 那么通过算法会生成相同的键值, 当 Map 缓存对象中己经存在该键值时, 则会返回 缓存中的对象;
任何的 INSERT ,UPDATE , DELETE 操作都会清空一级缓存;
二级缓存
二级缓存 (也叫应用缓存):
二级缓存存在于 SqlSessionFactory 的生命周期中, 可以理解为跨 sqlSession; 缓存是以 namespace 为单位的, 不同 namespace 下的操作互不影响.
setting 参数 cacheEnabled, 这个参数是二级缓存的全局开关, 默认值是 true, 如果把这个参数设置为 false, 即使有后面的二级缓存配置, 也不会生效;
要开启二级缓存, 你需要在你的 SQL 映射文件中添加配置:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
字面上看就是这样. 这个简单语句的效果如下:
映射语句文件中的所有 select 语句将会被缓存.
映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存.
缓存会使用 Least Recently Used(LRU, 最近最少使用的)算法来收回.
根据时间表(比如 no Flush Interval, 没有刷新间隔), 缓存不会以任何时间顺序 来刷新.
缓存会存储列表集合或对象 (无论查询方法返回什么) 的 1024 个引用.
缓存会被视为是 read/write(可读 / 可写)的缓存, 意味着对象检索不是共享的, 而 且可以安全地被调用者修改, 而不干扰其他调用者或线程所做的潜在修改.
Tips: 使用二级缓存容易出现脏读, 建议避免使用二级缓存, 在业务层使用可控制的缓存代替更好;
二级缓存也可以这么配置(整个 mapper 中的 sql 都会开启二级缓存功能, 并且还可以使用 cache-ref 引用其他 mapper):
- <mapper namespace="com.enjoylearning.mybatis.mapper.TUserMapper">
- <cache></cache>
- </mapper>
缓存示意图
MyBatis 集成 Spring
集成配置最佳实践
1. 准备 spring 项目一个
2. 在 pom 文件中添加 mybatis-spring 的依赖
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis-spring</artifactId>
- <version>1.3.0</version>
- </dependency>
3. 配置 SqlSessionFactoryBean
4. 配置 MapperScannerConfigurer
5. 配置事务
SqlSessionFactoryBean
在 MyBatis-Spring 中, SqlSessionFactoryBean 是用于创建 SqlSessionFactory 的.
dataSource : 用于配置数据源, 该属性为必选项, 必须通过这个属性配置数据源 , 这里使用了 上一节中配置好的 dataSource 数据库连接池 .
mapper Locations : 配置 SqlSessionFactoryBean 扫描 xml 映射文件的路径, 可以使用 Ant 风格的路径进行配置.
configLocation : 用于配置 mybatis config xml 的路径, 除了数据源外, 对 MyBatis 的各种配 直仍然可以通过这种方式进行, 并且配置 MyBatis settings 时只能使用这种方式. 但配置文件中 任意环境, 数据源 和 MyBatis 的事务管理器都会被忽略;
typeAliasesPackage : 配置包中类的别名, 配置后, 包中的类在 xml 映射文件中使用时可以 省略包名部分 , 直接使用类名. 这个配置不支持 Ant 风格的路径, 当需要配置多个包路径时可以 使用分号或逗号进行分隔 .
MapperScannerConfigurer
通过 MapperScannerConfigurer 类自动扫描所有的 Mapper 接口, 使用时可以直接注入接口 .MapperScannerConfigurer 中常配置以下两个属性 .
basePackage : 用于配置基本的包路径. 可以使用分号或逗号作为分隔符设置多于一个的包路 径, 每个映射器将会在指定的包路径中递归地被搜索到 .
annotationClass : 用于过滤被扫描的接口, 如果设置了该属性, 那么 MyBatis 的接口只有包 含该注解才会被扫描进去
mybatis 专题(二)----- 代码生成器, 关联查询, 缓存
来源: http://www.bubuko.com/infodetail-3415313.html