MyBatis主配置文件
1. Properties
可以通过resource属性指定properties配置文件,而后,在配置文件中可以通过${}的方式访问properties里的数据
2. Settings
可以通过setting标签来设置属性。
例:
下面的配置可以将数据库中的下划线式命名法映射到POJO类的驼峰命名法。
<setting name="mapUnderscoreToCamelCase" value="true" />
---------------------------------------------------------------------------------------
效果:
last_name ===> lastName
3. typeAliases
可以通过配置来给类的的全类名取别名
配置别名的两种方式:
- 通过typeAlias标签:
<typeAlias type="全类名" alias="别名"/>
alias标签可以省略,若没设置alias属性的话,则别名默认为类名
- 通过package标签:
<package name="包名"/>
指定包名里的所有类都会被设置默认别名,可能出现的问题是,如果指定包里存在着全类名不同但类名相同的类,使用别名时会出现冲突
此时可以使用@Alias注解来为需要的类配置新的别名,从而解决冲突
PS:别名不区分大小写
PS2:mybatis内置别名
别名 | 类型 |
---|---|
_byte | byte |
_short | short |
_int | int |
_integer | int |
_long | long |
_float | float |
_double | double |
_boolean | boolean |
string | String |
byte | Byte |
short | Short |
int | Integer |
integer | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
map | Map |
typeHandler
类型处理器:将数据库的数据类型与Java的数据类型一一对应,mybatis3.4以后就自带处理了,这里就不多学了。
plugins
插件
environments
环境标签,可以通过environment来配置一个具体的环境。在存在多个不同的环境时,可以通过environments的default属性来指定所使用的环境。
environment标签中,有两个标签
transactionManager:事务管理器
type:事务管理器的类型
可选值有JDBC(JdbcTransactionFactory)和MANAGED(ManagedTransactionFactory)
实际上这两个都是实现了TransactionFactory接口的类,通过实现TransactionFactory接口,也可以做到自定义事务管理器,此时type指定为类的全类名。
dataSource:数据源
type:数据源类型
可选值有UNPOOLED(UnpooledDataSourceFactory),POOLED(PooledDataSourceFactory),JNDI(JndiDataSourceFactory)。实质上是实现了DataSourceFactory接口的类。
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<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>
<environment id="yousql">
<transactionManager type="managed"></transactionManager>
<dataSource type="unpooled"></dataSource>
</environment>
</environments>
databaseIdProvider
作用:为sql语句选择使用的数据库,实现了多厂商支持
type:获得各数据库厂商的标识,例如MySQL,Oracle,SQL Server
property子标签可以给这些标识加上别名,然后在编写sql语句的时候就可以在属性指定databaseId这一属性。
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
=======================================================
用例:
<select id="selectOneUser" parameterType="_iNT" resultType="user" databaseId="mysql">
select * from tb_user where user_id = #{userId}
</select>
mappers
作用:将写好的sql映射文件注册到mybatis配置文件。
注册映射文件的方法:
- 通过mapper标签:
- resource:类路径
- url:网络路径或磁盘路径
- class:对应接口的路径
PS:使用class属性时,必须要有与接口对应的sql映射文件,而且必须和接口放在同一目录下。
- 通过package标签:
name:指定包名,批量注册指定包里的所有接口 - 不写映射文件,直接通过注解完成sql语句。
PS:如果通过mapper标签的class属性或者通过package标签来注册,同时又想实现接口和映射文件分离,那么可以通过在项目的资源目录resouces下创建和接口相同的包名,用来存放映射文件。这样做的原理是,这两个目录所指向的其实是同一个目录。
映射文件
增删改
- mybatis允许增删改定义以下返回值
int,long,boolean(基础类型、包装类) - 可以不写parameterType
但是写了可以限制传入的参数。 - 如果使用无参的openSession(),增删改操作时,需要手动提交。
- 获取自增主键的值
插入命令时,可以通过将useGeneratedKeys的值设为true,并且用keyProperty来指定用来返回主键值的JavaBean的一个属性。
参数处理
当传入一个参数时,mybatis不会对参数进行处理。
因此不管#{}里面填的是什么,都能取到唯一的参数当传入多个参数时,mybatis会将参数封装成map,如果不指定key的话,则map默认的key值为param1.....paramN,value值则为传入的参数值。
因此取值的时候可以通过#{默认的key值}或者#{索引}来找到对应的值。
但是也可以通过@param("设定的key")来给map设定key值。
用例:
User seleceByIdAndName(@Param("userId") int id, @Param("lastName") String name);
如果传入的多个参数正好是业务逻辑的模型,可以将这些参数封装成一个POJO对象传入
如果传入的多个参数不是业务逻辑的模型,且使用频率不高时,可以构造一个map
如果传入的多个参数不是业务逻辑的模型,但是使用频率很高时,可以编写TO(Transfer Object)数据传输对象。
Page {
int page;
int index;
}当传入的是Collection、List或者Array数组时:
Collection使用collection
List使用list、collection
Array使用array
这几个并不能使用通用的param1等
mybatis封装参数的过程。
- 判断参数的数量,如果为参数为0直接返回。
#{}和${}的区别
两者都是用来获取map中的值或者是POJO类中的属性。
#{}采用预编译的方式,将里面的值设置到sql语句中,类似于PreparedStatement。
${}采用字符串拼接的方式,将里面的值与sql语句进行拼接,有安全问题。
如果遇到原生JDBC都不支持占位符的情况下
比如分表,表名,排序。
select * from ${year}_salary;
select * from ${table_name};
这种情况下只能只用${}
通常情况下#{}比${}要安全,能用#{}的情况下选择用#{}
#{}在取值的时候还能加上其他参数,这里暂时先不学了。
返回值处理
通过resultType来指定返回值的类型
- 返回值是List<User>
此时resultType应该指定的是List中元素的类型,mybatis会把每一条记录封装成user对象,然后返回集合。 - 返回类型为Map<String, Object>
此时resultType是map,mybatis把记录拆成键值的方式封装在map里 - 返回类型是Map<String, User>
此时resultType应指定为User,因为mybatis是要将记录封装在User对象中,但是如果只指定了resultType,在运行时会报错,报错信息为:
Expected one result (or null) to be returned by selectOne(), but found: 2
需要返回的参数超出了mybatis能返回的数量,这时可以通过@MapKey来给接口中的方法打上注解,可以将JavaBean中的一个属性设定为另一个参数。
自定义返回类型
resultMap:不能和resultType同时使用,自定义封装规则
- type:指定自定义规则的JavaBean
- id:该resultMap的唯一标识符
子标签: - <id>:添加主键的映射
- <result>:添加其他字段的映射
用column属性指定数据库中的字段,property属性指定JavaBean中的成员。
id也可以用result替代,但是id的优化优于result
字段与JavaBean中成员名相同的,可以不进行映射。
当一个类中包含其他类的引用时,使用级联属性封装结果集
JavaBean(省略成员方法):
======================================
public class User {
private Integer userId;
private String lastName;
private String sex;
private Integer age;
private Class aClass;
}
public class Class {
private int id;
private String className;
}
映射文件:
======================================
<resultMap id="DifUser" type="ltc.model.User">
<id property="userId" column="user_id"/>
<result property="aClass.id" column="d_id"/>
<result property="aClass.className" column="class_name"/>
</resultMap>
<select id="getFullUserById" resultMap="DifUser">
select user_id,last_name,sex,age,d_id,class_name
from tb_user u, tb_class c where d_id = id and user_id = #{id}
</select>
这里将d_id字段的值交给了aClass的id属性,class_name交给了className属性。
当一个类中包含其他类的引用时,使用Association封装结果集
JavaBean同上,映射文件改为:
<resultMap id="Association" type="ltc.model.User">
<id property="userId" column="user_id"/>
<result property="lastName" column="last_name"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<association property="aClass" javaType="ltc.model.Class">
<id column="d_id" property="id"/>
<result column="class_name" property="className"/>
</association>
</resultMap>
<select id="getFullUserById" resultMap="Association">
select user_id,last_name,sex,age,d_id,class_name
from tb_user u, tb_class c where d_id = id and user_id = #{id}
</select>
association:
- property中的是JavaBean中被选为Association的属性,这里是aClass
- javaType中的是该属性的Java类型。
这里不把其他字段映射的话,获取不到值
使用association进行分步查询
步骤:
- 通过传入的userId值来获得user的详细信息
- 通过user信息中d_id来查询班级信息
- 将班级信息封装给user的aClass属性
<resultMap id="step" type="ltc.model.User">
<id property="userId" column="user_id"/>
<association property="aClass" select="ltc.dao.ClassMapper.getClassById" column="d_id">
</association>
</resultMap>
<select id="getFullUserByIdStep" resultMap="step">
select * from tb_user where user_id = #{id}
</select>
association中:
- select属性指定了要执行的查询语句
- column指定了执行语句所需的参数
分布查询的延迟加载:
仅当关联的属性被使用时,才执行分步的查询语句。
通过在mybatis配置文件中配置lazyLoadingEnabled和aggressiveLazyLoading属性。
该项设为true时,启动延迟加载
<setting name="lazyLoadingEnabled" value="true"/>
该项设为true时,在延迟加载时,一次性加载所有的属性,否则,只加载需要的属性。
<setting name="aggressiveLazyLoading" value="false"/>
返回值中包含集合
返回值中包含集合的时候,使用collection标签来对记录进行封装。
- property:封装成的属性
- ofType:分装成的Java类型
collection内的配置与以往相同。
<resultMap id="mycol" type="ltc.model.Class">
<id column="id" property="id"/>
<result column="class_name" property="className"/>
<collection property="users" ofType="ltc.model.User">
<id column="user_id" property="userId"/>
<result column="last_name" property="lastName"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
</collection>
</resultMap>
<select id="getClassByIdPlus" resultMap="mycol">
select c.id id,c.class_name class_name,u.user_id user_id,
u.last_name last_name, u.sex sex, u.age age
from tb_class c
left join tb_user u
on c.id = u.d_id
where c.id=#{id};
</select>
包含集合的分步查询
步骤:
- 用班级id获取到班级的信息
- 再用班级id在用户表里查找用户。
<resultMap id="classStep" type="ltc.model.Class">
<id column="id" property="id"/>
<result column="class_name" property="className"/>
<collection property="users" select="ltc.dao.ClassMapper.getAllUserById"
column="id" fetchType="lazy" >
</collection>
</resultMap>
<select id="getClassByIdStep" resultMap="classStep">
select * from tb_class where id = #{id}
</select>
<select id="getAllUserById" resultType="ltc.model.User">
select * from tb_user where d_id = #{id}
</select>
- fetchType:加载方式:延迟加载或立即加载
PS:当collection里的column要传递多个参数时,可以使用{key1=value1,key2=value2}的方式书写
<collection property="users" select="ltc.dao.ClassMapper.getAllUserById" column="{id=id}">
</collection>
注:这里第一次看视频教程的时候,视频上用的collection,但是我自己敲的时候不小心敲成association,但是也成功查询到了结果,因此目前有点分不清association和collection了。
动态SQL
if标签的使用
<if test=""></if>
与jstl表达式类似,不过OGNL里test支持的元素更多。
当满足test属性里的条件时,对应的sql语句片段被拼接
select * from tb_user
where
<!--test属性里的userId是直接从参数里取得值-->
<if test="userId != null">
user_id = #{userId}
</if>
<!--特殊符号应该使用转义符-->
<if test="lastName != null && lastName != """>
and last_name like #{lastName}
</if>
<if test="sex != null ">
and sex = #{sex}
</if>
<if test="age != null">
and age = #{age}
</if>
常用的字符转义符
字符 | 十进制 | 转义字符 |
---|---|---|
" | " | " |
& | & | & |
< | < | < |
> | > | > |
不断开空格(non-breaking space) |   | |
where标签
去掉拼接后字符串前多余的and或者or
<select id="getUsersWithDynamicSqlWhere" resultType="ltc.model.User">
select * from tb_user
<where>
<if test="userId != null">
user_id = #{userId}
</if>
<if test="lastName != null && lastName != """>
and last_name like #{lastName}
</if>
<if test="sex != null ">
and sex = #{sex}
</if>
<if test="age != null">
and age = #{age}
</if>
</where>
</select>
PS:
- where标签会自动在拼接体前面加上where
- where标签不能去除后面重复的and或or
trim标签
- prefix:前缀,在拼接体前面加上的前缀
- prefixOverrides:前缀覆盖,去掉拼接体前面多余的字符串
- suffix:后缀,在拼接体后面加上的后缀
- suffixOverrides:后缀覆盖,去掉拼接体后面多余的字符串
select * from tb_user
<trim prefix="where" suffixOverrides="and || or">
<if test="userId != null">
user_id = #{userId} and
</if>
<if test="lastName != null && lastName != """>
last_name like #{lastName} and
</if>
<if test="sex != null ">
sex = #{sex} and
</if>
<if test="age != null">
age = #{age}
</if>
</trim>
choose标签
当满足某一分支的时候选择对应的sql语句,类似于switch-case语句,按顺序判断
select * from tb_user
<where>
<choose>
<when test="userId != null">
user_id = #{userId}
</when>
<when test="lastName != null">
last_name like #{lastName}
</when>
<when test="sex != null">
sex = #{sex}
</when>
<when test="age != null">
age = #{age}
</when>
<!--当条件都不满足的时候返回所有user信息-->
<otherwise>
</otherwise>
</choose>
</where>
set标签
用于更新字段,当用if标签判断需要拼接的字符串时,可能出现多余的",",这个时候可以用set标签来取出多余的","。
<update id="updateUserById">
update tb_user
<set>
<if test="lastName != null">
last_name = #{lastName},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="age != null">
age = #{age}
</if>
</set>
<where>
user_id = #{userId}
</where>
</update>
foreach标签
通过遍历可以把集合里的元素生成一个字符串
- collection:用来选中传来的集合参数
- item:表示集合中每个元素
- separator:用来分个每个元素的符号
- open:在集合生成的字符串前面添加的字符串
- close:在集合生成的字符串后面添加的字符串
- index:表示遍历过程中,集合的索引(遍历map时,表示key值)
<select id="getUsersWithDynamicSqlForeach" resultType="ltc.model.User">
select * from tb_user
<where>
user_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select
使用foreach标签插入多条记录
- 通过mysql语句支持 insert into table_name values(),(),()的特点,用foreach标签拼接出来插入多条记录的单sql语句。
<insert id="insertUsersWithDynamicSqlForeach">
insert into tb_user(last_name, sex, age) values
<foreach collection="users" item="user" separator="," >
(#{user.lastName}, #{user.sex}, #{user.age})
</foreach>
</insert>
- 通过foreach拼接处多条用分号分个的insert语句
<insert id="insertUsersWithDynamicSqlForeach">
<foreach collection="users" item="user" separator=";">
insert into tb_user(last_name, sex, age)
values(#{user.lastName}, #{user.sex}, #{user.age})
</foreach>
</insert>
PS:这个方法不限于insert语句,也可以用于其他语句,但是使用这个方法必须在数据库连接url的属性后添加allowMultiQueries=true。
两个内置对象
- _parameter
表示传入sql语句的参数,多个参数时,这些参数会被封装成map,此时_parameter指代的就是这个map - _databaseId
表示当前数据库的id,可以通过这个对象判断当前使用的数据库
<select id="getUserTestInnerParamater" resultType="ltc.model.User">
<!--当当前数据库为mysql时,执行这个语句-->
<if test="_databaseId == mysql">
select * from tb_user
<!--当传入参数不为空时,才添加查找的条件-->
<if test="_parameter != null">
<where>
last_name = #{lastName}
</where>
</if>
</if>
<!--其他数据库时执行的语句-->
<if test="_databaseId == oracle">
</if>
</select>
bind标签
可以将OGNL表达式的值绑定到一个变量
<bind name="" value="'%' + lastName + '%'"/>
sql标签
- sql:抽取sql片段,以便后面重用
- include:引入sql标签声明的sql片段。
- include还可以通过property子标签设置自定义属性,可以在sql标签里通过${}直接使用。
<insert id="insertUsersWithDynamicSqlForeach">
<foreach collection="users" item="user" separator=";">
insert into tb_user(
<include refid="insertColumn"></include>
)
values(#{user.lastName}, #{user.sex}, #{user.age})
</foreach>
</insert>
<sql id="insertColumn">
last_name, sex, age
</sql>