Mybatis基础篇
Mybatis
mybatiis是一款持久层框架,支持自定义SQL、存储过程以及高级映射。让开发者只关注SQL语句本身,不用关注繁杂的注册驱动、等额外的代码。
MyBatis 可以通过
- 简单的 XML
- 注解
来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
Demo
依赖
导入mybatis jar包或者maven中引入依赖
1 | |
如果用的mysql,则需要导入mysql驱动依赖,根据版本修改version
1 | |
创建User.class、UserDao.class
User.class:User对象类,属性对应数据库t_user表中的字段
1 | |
UserDao.class:接口类,有两个方法
1 | |
从 XML 中构建 SqlSessionFactory
1. mybatis配置文件
在resource下新建mybatis-config.xml文件
environments:环境
mappers:配置mapper文件资源路径
1 | |
2. 创建UserDao的映射
两种方式都可以实现,貌似注解更省心。但当sql语句多且长时,注解就显得力不从心。
xml配置文件
一个接口类对应一个xml映射
namespace:命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。
命名解析:为了减少输入量,MyBatis 对所有具有名称的配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。
- 全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用。
- 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用全限定名。
1 | |
注解方式
1 | |
3. pom.xml配置xml资源加载路径
为了使项目编译时,将非resource下的xml配置文件也加入配置中,需要对pom.xml进行额外配置。加入如下build项。
1 | |
4. t_user表结构和数据
5. 新建测试类(SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的
1 | |
运行结果:
user = User{uId=’1’, uName=’zhang’, money=’1000’}
正确查出数据库数据。
6. 未提及事项及遇到的问题
- 注意要在pom.xml中配置资源解析路径,否则会提示xxx.xml资源文件未找到
- 注意数据库驱动版本
- 一个mapper中的namespace对应一个接口类;select id唯一,重复会报错已有映射;resultType要全路径,不然识别不了类
- 通常开发中,实体类对象的属性和数据表中的字段并非一一对应,此时可以配置映射来解决。
不使用 XML 构建 SqlSessionFactory
可以不使用xml配置,例子如下:
1 | |
作用域和生命周期
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
1 | |
在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。
映射器实例
映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:
1 | |
XML配置
属性(properties)
属性可以在外部进行配置,并可以进行动态替换。可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
1 | |
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值
1 | |
如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:
- 首先读取在 properties 元素体内指定的属性。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
默认值
从 MyBatis 3.4.2 开始,可以为占位符指定一个默认值。
1 | |
特性默认关闭, 要添加一个特性开启:
1 | |
设置(settings)
MyBatis 中极为重要的调整设置,会改变Mybatis的运行时行为。
类型别名(typeAliases)
类
可为Java类型设置一个缩写的别名,仅用于XML配置。类似一个字符串替换效果
1 | |
包
也可以配置一个包,MyBatis会在包下搜索需要的Java Bean,例如
1 | |
每一个在包 to.nexur.Pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 to.nexur.Pojo.User 的别名为 user;若有注解,则别名为其注解值。见下面的例子:
1 | |
类型处理器(typeHandlers)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
| 类型处理器 | Java 类型 | JDBC 类型 |
|---|---|---|
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 |
... |
... |
... |
事务管理器(transactionManager)
XML 映射器
MyBatis 的真正强大在于它的语句映射,映射器的 XML 文件就显得相对简单,减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
cache– 该命名空间的缓存配置。cache-ref– 引用其它命名空间的缓存配置。resultMap– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。sql– 可被其它语句引用的可重用语句块。insert– 映射插入语句。update– 映射更新语句。delete– 映射删除语句。select– 映射查询语句。
select
查询语句是mybatis中最常用的元素之一。
1 | |
语句名是selectPerson,接受的参数类型是int(Integer),并返回一个HashMap类型的对象,键是列名,值是结果行中的对应值。参数#{id}是预处理占位符,类似于
1 | |
Select 元素的属性
允许配置很多属性对每条语句行为细节控制
1 | |
| 属性 | 描述 |
|---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
| parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
resultType |
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap |
对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache |
将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
fetchSize |
这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType |
FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
resultOrdered |
这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false。 |
resultSets |
这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
insert, update 和 delete
数据变更语句 insert,update 和 delete 的实现非常接近:
1 | |
Insert, Update, Delete 元素的属性
| 属性 | 描述 |
|---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
参数
之前的语句都使用了简单的参数形式,如果 User 类型的参数对象传递到了语句中,会查找 id、username 和 password 属性,然后将它们的值传入预处理语句的参数中。
1 | |
和 MyBatis 的其它部分一样,参数也可以指定一个特殊的数据类型。
1 | |
对象是一个 HashMap。这个时候,需要显式指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。
1 | |
设置 numericScale 指定小数点后保留的位数。
1 | |
尽管上面这些选项很强大,但大多时候,只须简单指定属性名,顶多要为可能为空的列指定 jdbcType,其他的事情交给 MyBatis 自己去推断就行了。
1 | |
字符串替换
用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。
有时你就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句
1 | |
这样MyBatis 就不会修改或转义该字符串了。
当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用。 举个例子,如果你想 select 一个表任意一列的数据时,不需要这样写:
1 | |
而是可以只写这样一个方法:
1 | |
结果映射
resultMap 元素是 MyBatis 中最重要最强大的元素。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
假如一个Pojo类User,基于 JavaBean 的规范,这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。
这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。
1 | |
类别名
类型别名是你的好帮手。使用它们,你就可以不用输入类的全限定名了。比如:
1 | |
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!