mybatis-notes
Mybatis笔记
自定义Mybatis框架
配置文件相关类结构及作用
Database Access Object相关类
mybatis核心类
Mybatis的配置文件
全局配置文件
一般放于项目根目录下,名为SqlMapConfig.xml,其中记录了数据库的连接信息(driver url username password),以及映射文件的位置
配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
- 示例全局配置文件:
1 |
|
properties
1 | <properties resource="dbconfig.properties" /> |
如果属性在不只一个地方进行了配置,那么MyBatis 将按照下面的顺序来加载:
- 在properties 元素体内指定的属性首先被读取。
- 然后根据properties 元素中的resource 属性读取类路径下属性文件或根据url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
settings
1 | <settings> |
这是MyBatis 中极为重要的调整设置,它们会改变MyBatis的运行时行为。
https://mybatis.org/mybatis-3/zh/configuration.html#settings
类型别名(typeAliases)
类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
1 | <typeAliases> |
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
1 | <typeAliases> |
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
1 |
|
MyBatis已经为许多常见的Java类型内建了相应的类型别名。它们都是大小写不敏感的,用户使用时应该避免别名发生重复
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
typeHandlers类型处理器
无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。
BooleanTypeHandler |
java.lang.Boolean, boolean |
数据库兼容的 BOOLEAN |
|---|---|---|
ByteTypeHandler |
java.lang.Byte, byte |
数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler |
java.lang.Short, short |
数据库兼容的 NUMERIC 或 SMALLINT |
IntegerTypeHandler |
java.lang.Integer, int |
数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler |
java.lang.Long, long |
数据库兼容的 NUMERIC 或 BIGINT |
FloatTypeHandler |
java.lang.Float, float |
数据库兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler |
java.lang.Double, double |
数据库兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler |
java.math.BigDecimal |
数据库兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler |
java.lang.String |
CHAR, VARCHAR |
ClobReaderTypeHandler |
java.io.Reader |
- |
plugins插件
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。
- Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler(getParameterObject, setParameters)
- ResultSetHandler(handleResultSets, handleOutputParameters)
- StatementHandler(prepare, parameterize, batch, update, query)
environments环境
- MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。
- 每种环境使用一个environment标签进行配置并指定唯一标识符
- 可以通过environments标签中的default属性指定一个环境的标识符来快速的切换环境
environment具体环境
- id: 指定当前环境的唯一标识
- transactionManager: 事务管理器
- dataSource: 数据源,需要连接数据库相关的属性
1 | <environment id="mysql"> |
NOTE: 一般把数据库连接相关的配置放于外置的properties文件中,然后在Mybatis的主配置文件中引用.${username}的引用将返回当前计算机的用户名,而不是properties文件中的值
transactionManager
type:JDBC | MANAGED | 自定义
- JDBC:使用了JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围。JdbcTransactionFactory
- MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。ManagedTransactionFactory
- 自定义:实现TransactionFactory接口,type=全类名/别名
dataSource
type:UNPOOLED | POOLED | JNDI | 自定义
- UNPOOLED:不使用连接池,UnpooledDataSourceFactory
- POOLED:使用连接池,PooledDataSourceFactory
- JNDI:在EJB 或应用服务器这类容器中查找指定的数据源
- 自定义:实现DataSourceFactory接口,定义数据源的获取方式。
实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
数据库厂商标识databaseIdProvider标签
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:
1 | <databaseIdProvider type="DB_VENDOR"> |
- Type:DB_VENDOR
使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义。 - Property-name:数据库厂商标识
- Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用
映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。 但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:
1 | <!-- 使用相对于类路径的资源引用 --> |
这些配置会告诉了 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了
映射文件
可以放于任何目录下,一般放在dao接口同一级的目录下。该文件中记录了需要映射的dao接口类,以及dao接口中方法和SQL语句等的对应关系
使用#{}作为输入参数的占位符,类似PreparedStatement中的?占位符
1 |
|
DML
Note
Mybaits中允许增删改接口直接定义以下类型返回值 Integer Long Boolean Void 及其 原生Java类型。Mybatis会将SQL影响的行数、是否成功(影响行数 > 0)自动返回
SqlSession默认禁用了自动提交,若需要自动提交,则在获取SqlSession时应使用
openSession(true)来启用这个SqlSession的自动提交参数类型parameterType可以省略
获取自增主键的值
jdbc中使用statement.getGenratedKeys()获取自增主键的值,Mybatis中在insert标签中设置userGeneratedKeys="true"可以使用自增主键获取主键值策略keyProperyt属性指定Mybatis获取到主键值后,将这个值封装给javabean中的哪个属性
获取非自增主键
在<insert>中,加入<selectKey>标签,可以在sql执行前或后执行指定sql并将值封装给javabean
参数处理
单个参数
SQL中的#{}中的内容可以任意
多个参数
Mybatis默认将多个参数封装为一个map,其key值为param1 param2 … paramN 以及 参数的索引 即param1 args0
value值为实参的值,所以映射xml中的SQL可以使用map的key来获取传入的参数值
可以在dao接口的形参前添加@Param(“value”)来明确指定map中key的值
如果参数是Collection(List Set)类型或者数组,mybatis也会把传入的list或数组封装在map中。
key:collection[index] list[index] array[index]
若存在多个集合,则使用param1[index] param2[index]来获取不同集合的指定索引处的值
DQL
返回的类型为List
mapper.xml中<select>中resultType属性为List中泛型的数据类型,如返回类型为List<User>,则resultType=”User”
返回类型为Map
Map<field, value>
resultType=”map”,Mybatis将直接将数据的字段名和值封装至一个Map中
Map<key, javabean>
resultType=”javabean” 将返回值封装为JavaBean对象 在dao接口对应的方法上使用@MapKey("JavaBeanProperty")指定将哪个JavaBean属性作为Map的key
JavaBean属性与数据库字段不同的解决方法
若实体类中的属性名与数据库中的字段名相同,则直接指定<select>标签中的resultType属性为实体类的全限定名即可
若实体类中的属性名与数据库中的字段名不同,则有以下几种解决方法:
- 实体类的属性
1 | private Integer userId; |
- 1.使SQL中查询字段的别名与实体类中属性名相同
1 | <select [...]> |
- 2.使用
<resultMap>标签设置封装实体类属性名与数据库字段名之间的对应关系
1 | <mapper> |
- 3.在全局配置文件中启用驼峰命名法自动映射
1 | <settings> |
| column | property |
|---|---|
| user_name | userName |
resultMap
联合查询封装结果集
- 使用级联属性封装结果集 #{property.property}
- 使用association标签指定联合的javabean对象封装规则 嵌套结果集
- 使用association分步查询封装
association
指定嵌套的查询或封装规则,封装为指定对象
entities:
Account:
1 | public class Account { |
User:
1 | public class User { |
POJO中的属性可能会是一个对象,我们可以使用联合查询,并以级联属性的方式封装对象:
1 | <resultMap id="accountMap2" type="account"> |
association-嵌套结果集:
1 | <resultMap id="accountMap" type="account"> |
association-分段查询:
- select:调用目标的方法查询当前属性的值
- column:将指定列的值传入目标方法
1 | <resultMap id="accountMap3" type="account"> |
collection
指定嵌套的封装规则,将多行结果封装为一个集合
collection嵌套结果集的方式,定义关联的集合类型元素的封装
使用collection标签定义关联的集合类型的属性封装规则
colums属性若需要传递多个列,则可以使用map进行封装 {key1=column1, key2=column2},key为接口函数的形参名
1 | <resultMap id="accountList" type="user"> |
collection标签也支持分步查询以及延迟加载
NOTE: association或者collection标签的fetchType=eager/lazy可以覆盖全局的延迟加载策略,指定立即加载(eager)或者延迟加载(lazy)
懒加载
在使用association进行分步查询的情况下,可以开启懒加载,在使用了相关属性时才会执行对应的SQL并封装
配置方式:
mybatis-conf.xml
1 | <setttings> |
| 设置名 | 描述 | 有效值 | 默认值 |
|---|---|---|---|
| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
| aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。 | true | false | false (在 3.4.1 及之前的版本默认值为 true) |
DynamicSQL
if
通过判断条件是否为true,来确定是否拼接text到SQL中
对象导航图语言(Object Graph Navigation Language)
1 | <if test="OGNL"> |
where
代替SQL中的where,通过判断条件数量来自动的去除条件表达式开头的and或者or
1 | <where> |
trim
自定义字符串截取
1 | <!-- |
若id为null,则mybatis不会将第二个条件语句的and拼接至SQL
choose/when
分支选择,类似带break的switch-case语句
1 | <select id="getEmpsByConditionChoose" resultType="employee"> |
set
用于更新SQL
1 | <set> |
1 | <update id="updateEmp"> |
1 | <update id="updateEmp"> |
foreach
1 | <!-- |
批量插入数据
- 使用
values (), (), ...的插入方式(MySQL支持)
1 | <insert id="addEmps"> |
- 生成多条insert语句来实现批量插入
设置数据库连接属性allowMultiQueries为true:
1 | mysql.url=jdbc:mysql://localhost:3306/mybatis02?allowMultiQueries=true |
1 | <insert id="addEmps"> |
Mybatis的内置参数
_databaseId
数据库标识的别名
_parameter
传入的参数,如果是单个参数则表示那个参数,如果是多个参数,则代表封装后的map
bind标签
可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
Mybatis中使用Dao实现类执行CRUD
在Dao实现类中调用SqlSession的selectList selectOne insert update delete等方法执行crud操作,具体的SQL操作仍然位于mapper.xml文件中。在使用SQLSession的CRUD方法时,需要传入Map<String, Mapper> mappers中的key值,即全限定类名.方法名和需要的参数对象。
SqlSession的CRUD方法中,insert update delete最终都调用SqlSession中的update方法,所以调用insert和delete方法与直接调用update方法等价。
SqlSession.getMapper()将返回一个jdk代理对象,该代理对象最终还是会使用SqlSession中的insert update selectList等方法。
缓存
一级缓存
一级缓存:(本地缓存):sqlSession级别的缓存。一级缓存是一直开启的;SqlSession级别的一个Map
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):
- sqlSession不同。
- sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
- sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
- sqlSession相同,手动清除了一级缓存(缓存清空)
二级缓存
二级缓存:(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
- 工作机制:
- 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
- 不同namespace查出的数据会放在自己对应的缓存中(map)
1 | sqlSession==> EmployeeMapper==>Employee |
效果:数据会从二级缓存中获取
查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
使用:
1、开启全局二级缓存配置,全局配置文件中设置:
1 | <setting name="cacheEnabled" value="true"/> |
2、去mapper.xml中配置使用二级缓存:
1 | <cache></cache> |
3、由于缓存的元素默认是非只读的,所以Mybatis会通过序列化/反序列化克隆一个缓存中的对象给用户,所以我们的POJO需要实现序列化接口
1 | <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> |
eviction:缓存的回收策略:
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
- 默认的是 LRU。
flushInterval:缓存刷新间隔
- 缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
- true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快 - false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
- true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
size:缓存存放多少元素;
type=””:指定自定义缓存的全类名;
实现Cache接口即可;
Note
当readOnly设置为true时,通过二级缓存获取的查询结果为同一个真实对象,任何对这个真实对象的修改都将影响二级缓存中该对象的值;
当readOnly为false时,两次通过二级缓存获取的对象不是同一个,并且JavaBean类需要实现Serializable接口
和缓存有关的设置/属性:
1、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
2、每个select标签都有useCache=”true”:
false:不使用缓存(一级缓存依然使用,二级缓存不使用)
3、【每个增删改标签的:flushCache=”true”:(一级二级都会清除)】
增删改执行完成后就会清楚缓存;
测试:flushCache=”true”:一级缓存就清空了;二级也会被清除;
查询标签:flushCache=”false”:
如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
4、sqlSession.clearCache();只是清楚当前session的一级缓存;
5、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;
STATEMENT:可以禁用一级缓存
Mybaits Generator
基于xml配置文件,自动的生成基本的domain实体类、dao接口类和对应的SQL映射文件
generatorConfig.xml
src/main/resources/generatorConfig.xml:
1 |
|
1 |
|
使用MySQL时,配置文件中指定schema失效的情况
使用mybatis generator插件生产代码时,如果数据库是MySQL 8.x 自定义的表与系统表有同名时,会自动生产两张表的对应代码,而且会有很多冲突和错误,此时设置table的schema也没有效果,需要在连接节点里面添加 属性:
1 | <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" |
原文链接:https://blog.csdn.net/hello_jiangdong/article/details/81512468
If you are using version 8.x of Connector/J you may notice that the generator attempts to generate code for tables in the MySql information schemas (sys, information_schema, performance_schema, etc.) This is probably not what you want! To disable this behavior, add the property “nullCatalogMeansCurrent=true” to your JDBC URL.
For example:
1 | <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/my_schema" |
运行
maven plugin:
1 | <project> |
根据条件查询
- 创建实体类对应的Example对象
- 通过Example对象创建一个criteria对象,用于设置查询条件
- 调用criteria的
and*()等方法添加条件 - 将Example对象作为参数,传递给dao接口代理对象mapper的
*ByExample()方法
1 |
|