1. MyBatis
1.1. 介绍
MyBatis是一个半自动的ORM(Object Relational Mapping,对象关系映射)框架,Mybatis是一个Java持久层框架,但不属于ORM框架,因为它不遵循JPA规范。Mybatis做的不是对象关系映射,而是方法和SQL语句之间的映射。它可以将SQL语句和应用程序解耦,通过配置SQL语句和应用程序的映射关系,可以实现使用应用程序对数据库进行操作。
1.2. 使用
- 创建Mybatis全局配置文件mybatis-config.xml,配置数据源、事务管理器、引入mapper文件等。
- 创建Mapper接口,每个方法对应着一条sql。
- 创建sql映射文件文件Mapper.xml,里面是sql语句和结果集对象的映射关系。
<mapper>
标签的namespace为对应Mapper接口的全限定名称,sql标签的id和Mapper接口的方法名一一对应。 - 创建SqlSession实例,通过
sqlSession.getMapper(Class<T> var1)
获取Mapper接口的代理类从而实现Mapper中的方法。
1.2.1. 全局配置文件
<settings></settings>
此标签包含影响Mybatis执行的配置,参考:https://mybatis.org/mybatis-3/zh/configuration.html#settings 一个完整的settings配置如下: ```xml
</settings>
2. typeAliases
此标签给Java类型配置别名。
也可以通过子标签`<package>`指定Java类型所在的包名,MyBatis自动为类生成别名,别名的值就是类名且不区分大小写,如果Java类型使用了`@Alias`注解,则使用注解配置的别名。
```xml
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
-
typeHandler
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。 https://mybatis.org/mybatis-3/zh/configuration.html#settings
-
plugins
-
environments
配置数据环境,MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。 https://mybatis.org/mybatis-3/zh/configuration.html#environments 示例:<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments>
-
mappers
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
也可以通过mappers中的子标签
<pakage name="包名">
来进行批量Mapper注册,如果是XML形式的Mapper文件,则要保证编译后Mapper文件要放在与Mapper接口同名的目录下,如下图所示。
1.2.2. Mappers文件
1.2.2.1. 参数
当参数类型是不同的类型,MyBatis对参数的处理不一样:
- 单个基本数据类型,如slect *from xxx where id=#{id} 如果只有一个参数,#{}内是可以任意值。
- 多个基本数据类型
MyBatis会将多个参数存入一个map,并按照参数顺序将对应的key取名为param1…paramN,取值时通过#{paramN}取出,更简单的做法是在Mapper接口的方法参数上加上
@Param
注解,指定参数的名称,则取出时可以#{Param注解值}直接取出。 - 单个POJO 直接通过#{属性名}就能取出对象某个属性的值。
- 单个Map
直接通过
#{key名}
就能取出某个key的值。
1.2.2.1.1. javaType与jdbcType
由于Java和数据库的数据类型不是兼容的,Mybatis在进行和数据库的数据交互时需要使用类型转换器对类型进行来回转换,MyBatis预定义了很多类型转换器,通常我们不需要显示指定类型转换器
跟类型转换器有关的两个属性:javaType与jdbcType。 一般情况下这两个属性不是必需的,但也有时候需要显示声明。
注意:
- 和 MyBatis 的其它部分一样,几乎总是可以根据参数对象的类型确定 javaType,除非该对象是一个 HashMap。这个时候,你需要显式指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。
- JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)。阅读 PreparedStatement.setNull()的 JavaDoc 来获取更多信息。
参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#Parameters
1.2.2.1.2. #{}
与${}
#{}
取出的值会以占位符的形式设置到SQL中,经过了转义,可以防止SQL注入。${}
取出的值不经过转义,直接拼接在SQL语句中,不能防止SQL注入,一般在查询的参数为元数据比如列名、表名时,可以使用${}。
1.2.2.2. insert
示例:
<insert id="add" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user(name,password,birthday) VALUES(#{name},#{password},#{birthday})
</insert>
- 获取自增字段的值:insert的属性useGeneratedKeys设为true,keyProperty是返回的自增值注入值的字段。
1.2.2.3. select
- resultType:指定结果集的每行封装的数据类型,即使方法返回值为集合,resultType也只需要指定集合元素的类型。
- resultMap:自定义结果集列和Java类型的字段映射关系。
示例:User对象有一个Role类型的字段,需要从数据库将User和对应的Role查询出来,并将结果封装到User对象中,Mapper可以这样配置:
<resultMap id="rmap2" type="com.huangbei.mybatis.domain.User"> <id column="uid" property="id"/> <result property="name" column="name"/> <result property="password" column="password"/> <result property="birthday" column="birthday"/> <result property="rigisterDate" column="rigister_date"/> <!--方式一:使用字段.字段属性名实现Role字段的封装--> <!--<result property="role.roleName" column="role_name"/> <result property="role.id" column="rid"/>--> <!--方式二:使用association标签实现封装,但必须指定字段的javaType--> <association property="role" javaType="com.huangbei.mybatis.domain.Role"> <id property="id" column="rid"/> <result column="role_name" property="roleName"/> </association> </resultMap> <select id="findAllWithRole" resultMap="rmap2"> SELECT user.id uid,name,password,register_date,role.id rid,role_name FROM user JOIN role ON user.rid=role.id </select>
如果是多表查询,对象的字段有集合类型,比如一个用户对应多个Role,User实体类里有一个字段为List
,这时应该使用` `标签代替上面的association标签。
1.2.2.4. 动态SQL
动态 SQL 是 MyBatis 的强大特性之一。Mybatis提供了四种类型的标签以便于编写动态SQL
- if
<if test="表达式"></if>
用来支持条件逻辑,当表达式的结果为true,if标签内的语句会被拼接到SQL中。<select id="getByConditionMap" resultType="User"> SELECT *FROM user <where> <if test="id!=null"> AND id=#{id} </if> <if test="name!=null"> AND name=#{name} </if> <if test="password!=null"> AND password=#{password} </if> </where> </select>
- choose (when, otherwise)
choose和when作用类似Java中的switch - trim (where, set)
- where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,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>
- 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>
有4个属性:- prefix:在trim标签对应的的SQL位置拼接prefix
- prefixOverrides:去除指定的前缀内容,如果有多个匹配,用管道符
|
隔开 - suffix:在trim标签对应的的SQL最后面拼接suffix
- suffixOverrides:去除指定的后缀内容
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim> <trim prefix="SET" suffixOverrides=","> ... </trim>
- where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
- foreach
foreach用来支持迭代,典型的应用场景为IN子句:
<select id="findByIds" parameterType="list" resultType="User"> SELECT *FROM user <where> -- 如果是数组,collection为array <foreach collection="list" open="id IN (" item="id" separator="," close=")"> #{id} </foreach> </where> </select>
1.2.2.5. SQL片段重用
sql标签标识一个sql语句,include标签引用一个sql语句,可以实现SQL重用。
<sql id="selectAllUser">
SELECT *FROM user
</sql>
<select id="getById" resultType="User">
<include refid="selectAllUser"></include>
where id = #{id}
</select>
1.2.2.6. typeHandlers
在mybatis全局配置文件中,<typeHandlers></typeHandlers>
的作用是注册全局的类型处理器。类型处理器的作用是将某个字段的参数数据类型转换成跟数据库一致的参数类型,常见的使用场景比如枚举类参数转int值、日期类转毫秒值等。
Mybatis在org.apache.ibatis.type
包下预定义了许多类型处理器可供使用。
1.2.2.6.1. 自定义typeHandler
如果MyBatis预定义的类型处理器无法满足需求,MyBatis提供了自定义类型处理器的方式。 自定义typeHandler的方法如下:
- 创建typeHandler类,继承MyBatis中的
BaseTypeHandler<T>
抽象类,泛型T为Java类型。 - 实现抽象类的4个方法
- 在mybatis全局配置文件中通过
<typeHandlers>
标签注册,如果不想全局配置,可以在Mapper文件中:对于增删改,设置参数时在#{}
中指定自定义的typeHandler的全类名;对于查询,需要定义resultMap,在<result>
标签中指定typeHandler。
Example:
<insert id="add">
INSERT INTO employee(ename,salary,hire_date)
VALUES(#{ename},#{salary},#{hireDate,typeHandler=com.huangbei.mybatis.mapper.typehandler.Date2LongHandler})
</insert>
<resultMap id="rmap1" type="com.huangbei.mybatis.domain.Employee">
<id column="id" property="id"/>
<result column="ename" property="ename"/>
<result column="salary" property="salary"/>
<result column="hire_date" property="hireDate" typeHandler="com.huangbei.mybatis.mapper.typehandler.Date2LongHandler"/>
</resultMap>
<select id="findAll" resultMap="rmap1">
SELECT *FROM employee
</select>
1.3. MyBatis-Spring配置
1.3.1. 依赖
MyBatis整合到Spring中需要引入依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
1.3.2. 配置
将Mybatis整合到Spring中时,可以使用第三方的数据库连接池,比如Druid、c3p0等。
<!--连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--MyBatis-Spring Session工厂-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config-spring.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer">
<property name="basePackage" value="com.huangbei.mybatis.mapper" />
</bean>
1.3.3. 配置事务
<!--事务管理配置-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="mypoint" expression="execution(* com.huangbei.mybatis.service.impl.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"/>
</aop:config>