Mybatis
1. 原理
配置基础配置XML,如SqlMapConfig.xml
配置映射器XML,如mapper.xml
解析XML,SqlSessionFactoryBuilder构建出SqlSessionFactory对象
SqlSessionFactory产生SqlSession对象
SqlSession下的四大对象
- Executor:执行器,由它统一调度其他三个对象来执行对应的SQL;
- StatementHandler:使用数据库的Statement执行操作;
- ParameterHandler:用于SQL对参数的处理;
- ResultHandler:进行最后数据集的封装返回处理;
// 配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 使用SqlSessionFactoryBuilder从xml配置文件中创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建数据库会话实例sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 查询单个/多个记录,根据用户id查询用户信息(namespace.select的id)
User user = sqlSession.selectOne("test.findUserById", 10);
List<User> list = sqlSession.selectList("test.findUserByName","小明");
// 插入user用户的信息
sqlSession.insert("test.insertUser", user);
// 删除用户
sqlSession.delete("test.deleteUserById",18);
// 更新用户
sqlSession.update("test.updateUser", user);
// 提交事务
sqlSession.commit();
1.1. 原始DAO开发
编写DAO接口类
编写DAO实现类,实现类注入成员属性sqlSessionFactory,方法结构如下
@Override
public User findUserById(Integer id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
// 释放资源
sqlSession.close();
return user;
}
缺点:由于SQLSession是线程不安全的,所以必须放在方法内,但同时就导致方法内有很多重复冗余的代码。
1.2. mapper代理开发
- 编写mapper.xml
- 编写mapper接口(类似于DAO接口)
- 通过SqlSession.getMapper(接口名.class)获取实现了该接口的代理类实例,即DAO实例。
两者需要遵循一些规范才能生效
- Mapper.xml文件中的namespace与mapper接口的类全限定名相同
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
2. 基础配置XML
2.1. 根元素configuration
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
2.2. 配置Properties
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
加载顺序:
- 先加载
<properties>
内的<property>
的属性 - 然后加载resource/url指定的文件内的属性,并覆盖已读同名属性
- 最后读取parameterType传进来的属性值,并覆盖已读同名属性
使用:配置了的属性可以用${属性名}
来调用,包括mapper.xml里也能使用
2.3. 配置settings
<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"/>
</settings>
参考:settings全部属性
2.4. 配置连接池和事务管理,如果在Spring里配置则可以不写
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理,事务由Mybatis控制 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jsbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
2.5. 加载映射文件
<mappers>
<!-- resource和url都能引用资源 -->
<mapper resource="sqlmap/mapper.xml"/>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<!-- 加载mapper接口,必须满足mapper代理模式规范,且xml文件名和接口类名相同,且在同一目录下-->
<mapper class="org.mybatis.builder.PostMapper"/>
<!-- 批量加载包内的mapper接口-->
<package name="org.mybatis.builder"/>
</mappers>
2.6. 默认别名
别名 | 映射的类型 |
---|---|
_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 |
2.7. 自定义别名
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="cn.itcast.mybatis.po.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<package name="cn.itcast.mybatis.po"/>
<package name="其它包"/>
</typeAliases>
3. 在Spring里配置Mybatis
<!-- 配置mybatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
<!-- mapper扫描 -->
<property name="mapperLocations" value="classpath:mybatis/*/*.xml"></property>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
</bean>
4. XML映射文件
4.1. 根元素mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper:根标签,namespace:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
</mapper>
4.2. statement配置
<!-- 添加用户 -->
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 删除用户 -->
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
<!-- 更新用户 -->
<update id="updateUser" parameterType="cn.itcast.mybatis.po.User">
update user set username=#{username},birthday=#{birthday}, sex=#{sex},address=#{address} where id=#{id}
</update>
<!-- 查询用户,也可以用resultMap来映射返回值 -->
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">
select * from user where id = #{id}
</select>
4.3. #{}和${}
#{}
占位符
- 进行java类型和jdbc类型转换
- 传值时会带
""
- 如果parameterType传入的是pojo类型,那么
#{}
中的变量名称必须是pojo中对应的属性.属性.属性... - 如果parameterType传入的是基础类型值,
#{}
括号中可以是value或其它名称 - 可以有效防止sql注入
${}
拼接符
- 不进行java类型和jdbc类型转换
- 传值时不带
""
- 如果parameterType传入的是pojo类型,那么
${}
中的变量名称必须是pojo中对应的属性.属性.属性... - 如果parameterType传输单个简单类型值,
${}
括号中只能是value
使用情景
- mybaties排序时使用order by动态参数时需要注意,使用
${}
而不用#{}
- 模糊查询:
LIKE CONCAT('%',#{value},'%')
或LIKE '%${value}%'
4.4. 定义resultMap
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="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"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
resultMap标签
- id:resultMap的标识,即statement内resultMap属性需要的值。
- type:映射的pojo的全限定类名,或类型别名。
- autoMapping:true(默认)或false。是否启动自动映射功能,true则自动查找与字段名小写同名的属性名,并调用setter方法。false则需要明确注明映射关系才会调用对应的setter方法。
- extends:继承指定标识的resultMap的映射
id子标签:用于设置主键字段和pojo属性的映射关系
result子标签:用于设置普通字段和pojo属性的映射关系
- property:pojo的属性名
- column:查询结果的字段名
constructor标签:使用指定参数列表的构造函数来实例化pojo。注意:其子元素顺序必须与参数列表顺序对应
- idArg子标签:标记该入参为主键
- arg子标签:标记该入参为普通字段(主键使用该子元素设置也是可以的)
- column:查询结果的字段名
- JavaType:入参的java类型名
association 标签:pojo内部pojo类型属性的映射
- property:该pojo类型属性的属性名
- javaType:该属性的pojo类型名或别名
- resultMap:使用指定标识的resultMap映射
- column:传入关联查询的字段名,多个用
,
隔开 - select:关联查询的statement的id
collection标签:对应于一对多的集合映射
- property:该集合属性的属性名
- javaType:该集合属性的java属性类名或别名
- ofType:该集合内存的pojo类型名或别名
- resultMap:使用指定标识的resultMap映射
- column:传入关联查询的字段名,多个用
,
隔开 - select:关联查询的statement的id
延迟加载:select指定的查询只有在该属性的get方法被调用的时候才会执行,并把结果映射给该参数。需配置以下设置。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
discriminator标签:鉴别器,根据某个字段的值来动态选择映射类型,如果都不符合则使用父标签定义的resultMap
- column:用来判断的字段的名称
javaType:该字段映射的javaType,用来指定判断策略
case子标签:当column字段的值符合value属性时,使用resultType或者resultMap指定的映射关系。
4.5. INSERT返回主键
第一种方式:使用useGeneratedKeys
<insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="userId" parameterType="com.chenzhou.mybatis.User">
insert into user(userName,password,comment)
values(#{userName},#{password},#{comment})
</insert>
- useGeneratedKeys:默认false,true启用主键自增
- keyProperty:把主键自增后的id写入parameterType实体类的哪个属性里。
- MySQL和SQLServer支持,Oracle不支持。
第二种方式:使用<selectKey>
获取自增id
<insert id="insertProduct" parameterType="domain.model.ProductBean" >
<selectKey resultType="java.lang.Long" order="AFTER" keyProperty="productId">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO t_product(productName,productDesrcible,merchantId)values(#{productName},#{productDesrcible},#{merchantId});
</insert>
SELECT LAST_INSERT_ID()
是查询刚刚插入的记录自增长的id的SQL语句- order:BEFORE或AFTER,表示
<selectKey>
在insert语句的之前还是之后执行 - keyProperty:把主键自增后的id写入parameterType实体类的哪个属性里。
- resultType:指定查询结果在java里的返回类型
第三种方式:使用<selectKey>
生成自定义id
<insert id="insertProduct" parameterType="domain.model.ProductBean" >
<selectKey resultType="java.lang.String" order="BEFORE" keyProperty="productId">
SELECT product_seq.nextval from dual
</selectKey>
INSERT INTO t_product(productId,productName,productDesrcible,merchantId)values(#{productId},#{productName},#{productDesrcible},#{merchantId});
</insert>
5. 动态SQL
5.1. if标签
<select id="findUserList" parameterType="user" resultType="user">
select * from user where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意:要做不等于空字符串校验。
5.2. choose(when,otherwise)标签
<select id="findUserInfoByOneParam" parameterType="Map" resultMap="UserInfoResult">
select * from userinfo
<choose>
<when test="searchBy=='department'">
where department=#{department}
</when>
<when test="searchBy=='position'">
where position=#{position}
</when>
<otherwise>
where gender=#{gender}
</otherwise>
</choose>
</select>
按顺序判断when中的条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行otherwise中的sql。
5.3. where/set/trim标签
where 标签:只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where标签也会将它们去除。
set标签:只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“SET”子句。而且,若语句的结尾为,
,set标签也会将它们去除。
trim标签:
- prefix:需要加在内部sql前面的前缀
- prefixoverride:指定需要去掉的内部sql的第一个前缀
- suffix:需要加在内部sql后面的后缀
- suffixoverride:指定需要去掉的内部sql的最后一个后缀
5.4. sql片段
sql标签:定义一个可以被反复使用的sql片段。属性id指定sql片段的唯一标识
include标签:引用一个sql片段。属性refid指定sql片段的id。通过namespace.片段id来引用其他xml内的片段
5.5. foreach标签
collection:指定pojo的集合属性名,如果parameterType是List类型时填“list”,是数组时填“array”
item:定义每次遍历生成的对象名,当为Map时指代值
index:定义遍历的索引名,当为Map时指代键
open:开始遍历时拼接的字符串
close:结束遍历时拼接的字符串
separator:便利对象之间拼接的字符串