Mybatis的全部
文章内容来自尚硅谷Mybatis课程评论中
md下载地址:[https://fuxingxing.lanzouw.com/iAvhE00vnxgj]
文章目录
- XML配置文件
- 一、属性(properties)
- 二、设置(settings)
- 三、类型别名(typeAliases)
- 四、类型处理器(typeHandlers)
- 五、环境配置(environments)
- 六、映射器(mappers)
- XML映射文件
- 动态SQL
- Java API
XML配置文件
一、属性(properties)
-
加载properties配置文件,允许替换配置文件属性值。
<properties resource="com/junki/config.properties"> <property name="username" value="root"/> <property name="password" value="root"/> </properties>
-
加载的配置文件属性可以在全文使用,此时使用的{password}实际上是property标签替换的属性值。
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
二、设置(settings)
- 用于设置MyBatis运行时行为。配置项列表如下:
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods )。 |
true | false | false (在 3.4.1 及之前的版本默认值为 true) |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要驱动支持)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE : 不做任何反应WARNING : 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN )FAILING : 映射失败 (抛出 SqlSessionException ) |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
defaultResultSetType | Specifies a scroll strategy when omit it per statement settings. (Since: 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(same behavior with ‘Not Set’) | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL, VARCHAR 或 OTHER。 | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言。 | 一个类型别名或完全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) |
一个类型别名或完全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值初始化的时候比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null 。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (如集合或关联)。(新增于 3.4.2) |
true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) |
true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) |
类型别名或者全类名. | 未设置 |
-
完整配置如下:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
三、类型别名(typeAliases)
-
通过类型别名可以为Java类型设置一个简短的名字。这个别名仅用于XML配置文件,目的是减少编写过多的类完全限定名。
<typeAliases> <typeAlias alias="User" type="com.junki.entity.User"/> <typeAlias alias="Order" type="com.junki.entity.Order"/> </typeAliases>
此时XML配置文件中,可以使用User代替com.junki.entity.User。
-
也可以配置包扫描,这样无需配置每一个类。
<typeAliases> <package name="com.junki.entity"/> </typeAliases>
这样com.junki.entity包下的所有实体类都可以使用首字母小写的别名来代替它的全限定名。
-
如果实体类上使用了注解配置别名,则会使用注解配置的别名。
@Alias("user") public class User { ... }
-
注意常见的Java数据类型,MyBatis已经定义好了别名。具体对应如下:
别名 映射的类型 _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提供了很多类型处理器,无论是在预处理sql语句时设置参数还是从结果集中获取数值,都需要使用到类型处理器。这些处理器也是可以被重写或者重新配置的。
-
下面着重强调枚举类型的处理。
枚举类型的处理器有两个:
EnumTypeHandler
和EnumOrdinalTypeHandler
。EnumTypeHandler
是默认使用的,将枚举类型以其name值进行保存。EnumOrdinalTypeHandler
将枚举类型以其ordinal值进行保存。全局配置如下:
<!-- mybatis-config.xml --> <typeHandlers> <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.junki.enums.UserType"/> </typeHandlers>
也可以在mapper配置文件中指定映射类型:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.junki.mapper.UserMapper"> <resultMap type="com.junki.entity.User" id="usermap"> <id column="id" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="user_type" property="userType" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> </resultMap> <select id="getUser" resultMap="usermap"> select * from user </select> <insert id="insert"> insert into user (id, username, password, user_type) values ( #{id}, #{username}, #{password}, #{userType, typeHandler=org.apache.ibatis.type.EnumTypeHandler} ) </insert> </mapper>
五、环境配置(environments)
-
MyBatis可以配置多个环境,这有利于将SQL语句映射多种数据库,也有利于区分开发、测试和生产环境。
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <environment id="production"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${production_driver}"/> <property name="url" value="${production_url}"/> <property name="username" value="${production_username}"/> <property name="password" value="${production_password}"/> </dataSource> </environment> </environments>
配置说明:
-
默认使用的环境 ID(比如:default=“development”)。
-
每个 environment 元素定义的环境 ID(比如:id=“development”)。
-
事务管理器的配置(比如:type=“JDBC”)。
-
数据源的配置(比如:type=“POOLED”)。
-
-
尽管可以配置多个环境,但是每个SqlSessionFactory实例只能选择一种环境。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
如果创建实例时不指定环境,将使用默认环境。
-
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
-
JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
-
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
-
如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
-
-
数据源(data Source)
有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
- POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
- JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用
也可以通过实现接口
org.apache.ibatis.datasource.DataSourceFactory
来使用第三方数据源,我们一般通过继承org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
来实现新的数据源适配器。import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0DataSourceFactory extends UnpooledDataSourceFactory { public C3P0DataSourceFactory() { this.dataSource = new ComboPooledDataSource(); } }
<dataSource type="com.junki.config.C3P0DataSourceFactory"> <property name="driver" value="org.postgresql.Driver"/> <property name="url" value="jdbc:postgresql:mydb"/> <property name="username" value="postgres"/> <property name="password" value="root"/> </dataSource>
六、映射器(mappers)
-
引入mapper映射配置文件,如下配置会告诉了 MyBatis 去哪里找映射文件。
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
XML映射文件
一、select
-
基本写法如下
<select id="getById" parameterType="int" resultType="hashmap"> SELECT * FROM user WHERE id = #{id} </select>
这个语句的名称是getById,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
这里的
#{id}
类同于JDBC代码的预编译插值。String sql = "SELECT * FROM user WHERE id=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1,id);
-
其他属性配置
<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
属性 描述 id
在命名空间中唯一的标识符,可以被用来引用这条语句。 parameterType
将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。 parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。 resultType
从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。 resultMap
外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。 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 id="insert"> insert into user (id,username,password) values (#{id},#{username},#{password}) </insert> <update id="update"> update user set username = #{username}, password = #{password} where id = #{id} </update> <delete id="delete"> delete from user where id = #{id} </delete>
-
其他属性配置
<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20"> <update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> <delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
属性 描述 id
命名空间中的唯一标识符,可被用来代表这条语句。 parameterType
将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。 parameterMap
这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。 flushCache
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。 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 的语句;如果带或者不带的语句都有,则不带的会被忽略。 -
主键自增
<insert id="insert" useGeneratedKeys="true" keyProperty="id"> insert into user (username,password) values (#{username},#{password}) </insert>
-
多行插入
<insert id="insert" useGeneratedKeys="true" keyProperty="id"> insert into user (username, password) values <foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}) </foreach> </insert>
-
生成随机id
<insert id="insert"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into user (id, username, password) values (#{id}, #{username}, #{password}) </insert>
属性 描述 keyProperty
selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 keyColumn
匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 resultType
结果的类型。MyBatis 通常可以推断出来,但是为了更加精确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。 order
这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先生成主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 statementType
与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。
三、sql
-
定义可重用的SQL代码段
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
这个 SQL 片段可以被包含在其他语句中,例如:
<select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="user"/></include> from user </select>
四、参数
-
简单数据类型参数
<select id="getById" parameterType="int" resultType="user"> SELECT id, username, password FROM user WHERE id = #{id} </select>
-
复杂数据类型参数
<insert id="insert" parameterType="User"> insert into user (id, username, password) values (#{id}, #{username}, #{password}) </insert>
如果 User 类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找,然后将它们的值传入预处理语句的参数中。
-
字符串替换
默认情况下,使用
#{}
格式的语法会导致 MyBatis 创建PreparedStatement
参数占位符并安全地设置参数(就像使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在 SQL 语句中插入一个不转义的字符串。 你可以这样来使用:@Select("select * from user where ${column} = #{value}") User findByColumn(@Param("column") String column, @Param("value") String value);
其中
${column}
会被直接替换,而#{value}
会被使用?
预处理。 因此你就可以像下面这样来达到上述功能:User user1 = userMapper.findByColumn("id", 1L); User user2 = userMapper.findByColumn("username", "junki"); User user3 = userMapper.findByColumn("password", "123456");
注意:用这种方式接受用户的输入,并将其用于语句中的参数是不安全的,会导致潜在的 SQL 注入攻击,因此要么不允许用户输入这些字段,要么自行转义并检验。
五、结果映射
-
将结果存储到map集合
<select id="getById" resultType="map"> select id, username, password from user where id = #{id} </select>
-
将结果封装到javabean
<select id="selectUsers" resultType="com.junki.entity.User"> select id, username, password from user where id = #{id} </select>
可以使用配置的类别名来代替全限定名
<!-- mybatis-config.xml 中 --> <typeAlias type="com.junki.entity.User" alias="User"/> <!-- SQL 映射 XML 中 --> <select id="getById" resultType="User"> select id, username, password from user where id = #{id} </select>
-
ResultMap可以很好的解决列名和属性名不匹配的问题
<resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="password"/> </resultMap>
此时使用
resultMap
属性代替resultType
属性<select id="selectUsers" resultMap="userResultMap"> select user_id, user_name, password from user where id = #{id} </select>
-
id & result
<id property="id" column="user_id"/> <result property="username" column="user_name"/>
这是结果映射最基本的内容。
-
构造方法
为了将结果注入构造方法,MyBatis 需要通过某种方式定位相应的构造方法。 在下面的例子中,MyBatis 搜索一个声明了三个形参的的构造方法,参数类型以
java.lang.Integer
,java.lang.String
和int
的顺序给出。<constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> <arg column="age" javaType="_int"/> </constructor>
-
关联
关联表示一对一的映射关系,一个博客有一个用户,此时在Blog实体类中有一个author属性,数据类型是User。映射配置如下:
<association property="author" column="author_id" javaType="User"> <id property="id" column="id"/> <result property="username" column="username"/> </association>
此时MyBatis会通过blog表中的author_id字段去关联查询user表中的id字段,并查询到user表中的id和username封装到User实体类并赋值给Blog实体类中的author属性。
属性 描述 property
映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 javaType
一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 jdbcType
JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。 typeHandler
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。 -
关联的嵌套Select查询
<resultMap id="blogResult" type="Blog"> <association property="author" column="author_id" javaType="User" select="getUserById"/> </resultMap> <select id="getBlogById" resultMap="blogResult"> SELECT * FROM blog WHERE id = #{id} </select> <select id="getUserById" resultType="User"> SELECT * FROM user WHERE id = #{id} </select>
属性 描述 column
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName)
方法的参数一样。 注意:在使用复合主键的时候,你可以使用column="{prop1=col1,prop2=col2}"
这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得prop1
和prop2
作为参数对象,被设置为对应嵌套 Select 语句的参数。select
用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}"
这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得prop1
和prop2
作为参数对象,被设置为对应嵌套 Select 语句的参数。fetchType
可选的。有效值为 lazy
和eager
。 指定属性后,将在映射中忽略全局配置参数lazyLoadingEnabled
,使用属性的值。这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 我们可以使用延迟加载,也可以使用以下两种方式解决。
-
关联的嵌套结果映射
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </resultMap>
属性 描述 resultMap
结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet
。这样的ResultSet
有部分数据是重复的。 为了将结果集正确地映射到嵌套的对象树中, MyBatis 允许你“串联”结果映射,以便解决嵌套结果集的问题。使用嵌套结果映射的一个例子在表格以后。columnPrefix
当连接多个表时,你可能会不得不使用列别名来避免在 ResultSet
中产生重复的列名。指定 columnPrefix 列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。 详细说明请参考后面的例子。notNullColumn
默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。 autoMapping
如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。注意,本属性对外部的结果映射无效,所以不能搭配 select
或resultMap
元素使用。默认值:未设 -
关联的多结果集(ResultSet)
属性 描述 column
当使用多个结果集时,该属性指定结果集中用于与 foreignColumn
匹配的列(多个列名以逗号隔开),以识别关系中的父类型与子类型。foreignColumn
指定外键对应的列名,指定的列将与父类型中 column
的给出的列进行匹配。resultSet
指定用于加载复杂类型的结果集名字。 <select id="getBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE"> {call getBlogsAndAuthors(#{id)} </select>
<resultMap id="blogResult" type="Blog"> <id property="id" column="id" /> <result property="title" column="title"/> <association property="author" javaType="User" resultSet="authors" column="author_id" foreignColumn="id"> <id property="id" column="id"/> <result property="username" column="username"/> <result property="password" column="password"/> </association> </resultMap>
-
集合
集合描述的是“一对多”的关系,如:一个博客有很多文章。
private List<Post> posts;
<collection property="posts" ofType="com.junki.entity.Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection>
-
集合的嵌套Select查询
<resultMap id="blogResult" type="Blog"> <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectPostsForBlog" resultType="Post"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select>
一般情况下MyBatis可以推断javaType属性,所以可以省略
<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>
-
集合的嵌套结果映射
如果使用如下查询语句,那么可以配置嵌套的结果映射来映射查询结果。
<select id="selectBlog" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, P.id as post_id, P.subject as post_subject, P.body as post_body, from Blog B left outer join Post P on B.id = P.blog_id where B.id = #{id} </select>
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection> </resultMap>
或者可以将映射写成如下形式:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="id"/> <result property="subject" column="subject"/> <result property="body" column="body"/> </resultMap>
六、自动映射
-
如下写法,没有手动映射的字段,会根据字段名称自动映射,有时我们可能需要在sql语句中通过as来起别名:
<select id="selectUsers" resultMap="userResultMap"> select user_id as "id", user_name as "userName", hashed_password from some_table where id = #{id} </select>
<resultMap id="userResultMap" type="User"> <result property="password" column="hashed_password"/> </resultMap>
-
自动映射等级有三个
-
NONE
- 禁用自动映射。仅对手动映射的属性进行映射。 -
PARTIAL
- 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射 -
FULL
- 自动映射所有属性。默认值是
PARTIAL
,这是有原因的。当对连接查询的结果使用FULL
时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。
-
-
我们可以这样设置自动映射等级
<resultMap id="userResultMap" type="User" autoMapping="false"> <result property="password" column="hashed_password"/> </resultMap>
七、缓存
-
MyBatis默认启用本地的会话缓存,它仅仅对一个会话中的数据进行缓存,如果想要开启全局的二级缓存,需要在sql映射文件中追加如下配置:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
eviction表示缓存清除策略。
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
-
进行缓存配置后,基本效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
-
使用自定义缓存
可以使用第三方缓存方案来创建适配器,来完全覆盖MyBatis的缓存行为。
参考案例:使用Redis做Mybatis的二级缓存
动态SQL
用过JDBC都知道,很多情况下都需要根据不同的条件拼接SQL语句,这是一个很繁杂的过程。MyBatis通过了一些列标签,可以简单的完成动态SQL的任务。
一、if
-
如下查询语句,如果title字段为null,就无需进行title的条件查询。
<select id="findActiveBlogWithTitleLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <if test="title != null"> AND title like #{title} </if> </select>
-
如果要根据作者姓名进行查询,建议先判断author是否null,再判断author.name是否为null
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </select>
二、choose,when,otherwise
-
if实现了多个查询条件的拼接,choose可以实现多个查询条件选择其一。
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND author_name like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select>
如果用户输入title,就根据title查询blog;如果没有title,有author.name就根据author_name查询blog;否则就默认查询精选blog。
三、trim,where,set
-
如果where语句中的所有条件都是动态sql,可能会出现语法错误,如下:
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </select>
如果state == null,就会得到如下语句:
SELECT * FROM BLOG WHERE AND title like #{title} AND author_name like #{author.name}
很明显,where之后接and是错误的。
-
为了解决如上问题,可以使用where做改写
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </where> </select>
where元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where元素也会将它们去除。
等同于trim的写法如下:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>
-
除了查询语句需要动态sql,更新语句可能也会需要,我们可以使用set标签来处理。
<update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>
等同于trim的写法如下:
<trim prefix="SET" suffixOverrides=","> ... </trim>
四、foreach
-
动态sql经常用到对集合的遍历
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
五、script
-
在带注解的映射器接口类中使用动态sql,可以使用script标签
@Update({"<script>", "update Author", " <set>", " <if test='username != null'>username=#{username},</if>", " <if test='password != null'>password=#{password},</if>", " <if test='email != null'>email=#{email},</if>", " <if test='bio != null'>bio=#{bio}</if>", " </set>", "where id=#{id}", "</script>"}) void updateAuthorValues(Author author);
六、bind
-
bind标签可以从OGNL表达式中创建一个变量并将其绑定到上下文。
<select id="selectBlogsLike" resultType="Blog"> <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" /> SELECT * FROM BLOG WHERE title LIKE #{pattern} </select>
Java API
一、SqlSession
使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通过这个接口来执行命令,获取映射器和管理事务。
SqlSession 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的所有方法。
而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或手动配置 Java 代码来创建 SqlSessionFactory。
-
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 有五个 build() 方法,每一种都允许你从不同的资源中创建一个 SqlSessionFactory 实例。
SqlSessionFactory build(InputStream inputStream) SqlSessionFactory build(InputStream inputStream, String environment) SqlSessionFactory build(InputStream inputStream, Properties properties) SqlSessionFactory build(InputStream inputStream, String env, Properties props) SqlSessionFactory build(Configuration config)
如果一个属性存在于这些位置,那么 MyBatis 将会按照下面的顺序来加载它们:
- 首先读取在 properties 元素体中指定的属性;
- 其次,读取从 properties 元素的类路径 resource 或 url 指定的属性,且会覆盖已经指定了的重复属性;
- 最后,读取作为方法参数传递的属性,且会覆盖已经从 properties 元素体和 resource 或 url 属性中加载了的重复属性。
-
第一种方式使用较多,实例如下:
String resource = "org/mybatis/builder/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream);
-
SqlSessionFactory
SqlSessionFactory 有六个方法创建 SqlSession 实例。通常来说,当你选择这些方法时你需要考虑以下几点:
- 事务处理:我需要在 session 使用事务或者使用自动提交功能(auto-commit)吗?(通常意味着很多数据库和/或 JDBC 驱动没有事务)
- 连接:我需要依赖 MyBatis 获得来自数据源的配置吗?还是使用自己提供的配置?
- 执行语句:我需要 MyBatis 复用预处理语句和/或批量更新语句(包括插入和删除)吗?
基于以上需求,有下列已重载的多个 openSession() 方法供使用。
SqlSession openSession() SqlSession openSession(boolean autoCommit) SqlSession openSession(Connection connection) SqlSession openSession(TransactionIsolationLevel level) SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) SqlSession openSession(ExecutorType execType) SqlSession openSession(ExecutorType execType, boolean autoCommit) SqlSession openSession(ExecutorType execType, Connection connection) Configuration getConfiguration();
默认的 openSession()方法没有参数,它会创建有如下特性的 SqlSession:
- 会开启一个事务(也就是不自动提交)。
- 将从由当前环境配置的 DataSource 实例中获取 Connection 对象。
- 事务隔离级别将会使用驱动或数据源的默认设置。
- 预处理语句不会被复用,也不会批量处理更新。
-
SqlSession
执行语句方法
<T> T selectOne(String statement, Object parameter) <E> List<E> selectList(String statement, Object parameter) <T> Cursor<T> selectCursor(String statement, Object parameter) <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey) int insert(String statement, Object parameter) int update(String statement, Object parameter) int delete(String statement, Object parameter)
批量立即更新方法
List<BatchResult> flushStatements()
事务控制方法
void commit() void commit(boolean force) void rollback() void rollback(boolean force)
清空本地缓存
void clearCache()
关闭SqlSession
void close()
使用映射器
<T> T getMapper(Class<T> type)
二、注解开发
-
Mybatis注解开发实例如下:
import org.apache.ibatis.annotations.*; import org.junki.entity.User; import java.util.List; /** * @author junki */ public interface UserMapper { // 插入 @Insert("insert into tb_user (name,sex,age) values (#{name},#{sex},#{age})") // useGeneratedKeys = true表示使用数据库自动增长的主键,需要底层数据库的支持。keyProperty = "id"表示将插入数据生成的主键设置到user对象的id当中 @Options(useGeneratedKeys = true,keyProperty = "id") int saveUser(User user); // 删除 @Delete("delete from tb_user where id = #{id}") int removeUser(@Param("id") Integer id); // 更新 @Update("update tb_user set name=#{name},age=#{age},sex=#{sex} where id = #{id}") void modifyUser(User user); // 根据ID查询 @Select("select * from tb_user where id= #{id}") // @Result注解用于列和属性之间的结果映射,如果列和属性名称相同,则可以省略该注解,MyBatis会自动进行映射 @Results({ @Result(id=true,column = "id",property = "id"), @Result(column = "name",property = "name"), @Result(column = "age",property = "age"), @Result(column = "sex",property = "sex"), }) User selectUserById(Integer id); // 查询所有 @Select("select * from tb_user") List<User> selectAllUser(); }
-
@SqlProvider注解使用案例
@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName") List<User> getUsersByName( @Param("name") String name, @Param("orderByColumn") String orderByColumn); class UserSqlBuilder { // If not use @Param, you should be define same arguments with mapper method public static String buildGetUsersByName( final String name, final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); WHERE("name like #{name} || '%'"); ORDER_BY(orderByColumn); }}.toString(); } // If use @Param, you can define only arguments to be used public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); WHERE("name like #{name} || '%'"); ORDER_BY(orderByColumn); }}.toString(); } }
-
注意:由于Java 注解的的表达力和灵活性十分有限,最强大的 MyBatis 映射并不能用注解来构建。
本文是在网上找的md格式笔记,这个笔记都对我的学习很有帮助,所以为了不永久保存,我上传自己的csdn中。这是
md下载地址:https://fuxingxing.lanzouw.com/iAvhE00vnxgj
文件非本人整理,侵权则删