一、Mybatis简介
MyBatis 是一个流行的 Java 持久层框架,它提供了一个简单但强大的 SQL 映射器,允许开发者将 SQL 语句与 Java 对象进行映射。MyBatis 封装了 JDBC 的操作,简化了数据库操作的复杂性,使得开发者可以更专注于业务逻辑。
二、Mybatis基本使用
快速入门(基本流程)
- 导入相关依赖:mybatis依赖、mysql驱动
- 准备mapper接口、mapper.xml配置文件
- 准备mybatis配置文件
- test中使用mybatis框架,需要先加载框架的配置文件:mybatis的配置文件,获得读取的输入流
- 基于输入流创建SqlSessionFactoryBuilder对象(创建一个工厂对象,用于生产出SqlSession对象,一个SqlSession相当于一个会话)
- 由会话对象(SqlSession),基于EmployeeMapper接口的Class对象获取Mapper接口类型的对象(动态代理技术)【获得代理对象】
- 由代理对象,调用代理类方法,从而触发sql语句。
简单说明:
-
mybatis依赖中可能会有ibatis的东西,知道就好
-
为什么要mapper接口?它是用来干什么的?
**ibatis:**之前ibatis框架的实现过程和上面大体相同,细节部分暂且不说,从宏观上讲,ibatis框架并没有动态代理那一步,直接采用SqlSession进行方法的调用。但是在方法调用时候,我们发现:方法调用时,需要提供sql的全限定名namespace和id让ibatis框架找到想要调用的方法,其中参数还只能传入一个,想要传入多个参数还要把他们封装为一个对象,或者使用map,总之很麻烦。
**mybatis:**mybatis比ibatis多了一个动态代理,目的就是为了优化这些问题,因此相当于对ibatis又进行了一次封装。在最初进行mapper.xml文件配置时规定namespace就是接口名,那么在调用时期mybatis框架就会通过代理类对象,反射自动的将namespace和方法名进行拼接,然后找到调用的方法,执行sql语句。总之,极大方便了程序员的调用。
向sql语句传参
mybatis日志输出配置
mybatis配置文件设计标签和顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
我们可以在mybatis的配置文件使用settings标签设置,输出运过程SQL日志!
注意:这个结构是有一定顺序的!!!!
日志配置:
<!--日志配置属性logImp 属性值SLF4j......-->
<settings>
<!-- SLF4J 选择slf4j输出! -->
<setting name="logImpl" value="SLF4J"/>
</settings>
<!--选择在控制台打出日志-->
<settings>
<!--开启了mybatis的日志输出功能-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
#{}和${}
Mybatis会将SQL语句中的#{}转换为问号占位符。
${}形式传参,底层Mybatis做的是字符串拼接操作。
通常不会采用${}的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用
结论:实际开发中,能用#{}实现的,肯定不用${}。
特殊情况: 动态的不是值,是列名或者关键字,需要使用${}拼接
注解方式:
//注解方式传入参数!!
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column,
@Param("value") String value);
数据输入
这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
简单类型:只包含一个值的数据类型
- 基本数据类型:int、byte、short、double、……
- 基本数据类型的包装类型:Integer、Character、Double、……
- 字符串类型:String
复杂类型:包含多个值的数据类型
- 实体类类型:Employee、Department、……
- 集合类型:List、Set、Map、……
- 数组类型:int[]、String[]、……
- 复合类型:List、实体类中包含集合……
下面就讨论各种数据类型怎么传入,其实就是在mapper.xml中key怎么写?
单个简单类型参数
Mapper接口中抽象方法的声明
Employee selectEmployee(Integer empId);
SQL语句
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select>
单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。
多个简单类型参数
不可以随便写,形参名称获取也不行;到底怎么写?
方案一:注解指定【推荐】 @Param直接在mapper接口里给方法中的每个参数“取别名” 然后直接就可以作为key
方案二:mybatis默认机制 形参列表直接从左往右(arg0,arg1,arg2...)
//注解方式
int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);
<update id="updateEmployee">
update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}
</update>
实体类参数
key就等于实体对象的属性名(驼峰命名形式)【必须的!!!】
原理:Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}解析后的问号占位符这个位置。
Map类型参数
传入map如何指定key值,key就是map的key
数据输出
数据无非有两种输出结果:
-
DML语句的受影响行行数:直接使用 int 或 long 类型接收即可
-
查询操作返回的结果集
我们需要的是指定查询输出的数据类型即可
并且插入场景下,实现主键回显示(就是可以获得主键,再利用)
实际上,下面讨论的就是**resultType(或者resultMap)**怎么写?
返回单个简单类型
resultType可以返回的形式:全限定符 || 别名 || 集合类型,写泛型
全限定符:全限定符包括 :包名 + 类名 ,比如:java.lang.String
是一个全限定符,指向的就是String类
集合类型:List,那么直接写Student就好了
别名:有三种方法:
- 在mybatis配置文件中用typeAliases标签和typeAliase标签,并直接指定别名和要起名的类
- 在mybatis配置文件中用typeAliases标签和typeAliase标签,并指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,怎么找?它会在包下检索每个类的
首字母小写的名字(简单的说就是找类的首字母小写,因为一般类的名字都是大写 )
- 直接使用注解,@Alias(“别名”)
补充:mybatis有内置的类型别名,如下
下面是Mybatis为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_char (since 3.5.10) | char |
_character (since 3.5.10) | char |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
char (since 3.5.10) | Character |
character (since 3.5.10) | Character |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
biginteger | BigInteger |
object | Object |
object[] | Object[] |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
返回实体类对象
resultType属性:指定封装查询结果的Java实体类的全类名
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
<!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
<!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}
</select>
**注意:**通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
增加全局配置自动识别对应关系
在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名
<!-- 在全局范围内对Mybatis进行配置 -->
<settings>
<!-- 具体配置 -->
<!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
<!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
<!-- 规则要求数据库表字段命名方式:单词_单词 -->
<!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
返回map类型
适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。所以我们需要学习Map类型
返回的类型无法对应我们现有的实体类,因此我们返回map集合,怎么表示返回的是一个map集合呢?
两步骤:
- 定义映射关系,使用resultMap标签
<resultMap id="blogResultMap" type="java.util.HashMap">
<!-- 配置列名和Map中的键的映射 -->
<result property="id" column="blog_id" />
<result property="title" column="title" />
<!-- 其他字段映射 -->
</resultMap>
id:id可以随便起,但是一般建议具有实际意义或者直接和map集合名对应
type:用于指定返回的map的具体类型
property:指定map的key,列名就是数据库中的字段名
<!--将数据库列名映射到Map的key,这样在处理查询结果时,可以通过这些key来获取数据。-->
- 在查询语句中,引用resultMap映射的id
<!-- Map<String,Object> selectEmpNameAndMaxSalary(); -->
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>
id:方法名
resultType:返回类型,就是刚才的map的ID
返回List类型
查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
<!-- List<Employee> selectAll(); -->
<select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp
</select>
后面可以通过起别名的方式,简写那个实体类类型,一般就是批量处理包,默认就是类名的先后字母小写,然后就可以直接写为employee
返回主键值
-
自增长类型主键
Mapper接口中的抽象方法
int insertEmployee(Employee employee);
SQL语句
<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定实体类的某一属性名作为主键值,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">
insert into t_emp(emp_name,emp_salary)
values(#{empName},#{empSalary})
</insert>
junit测试
@Test
public void testSaveEmp() {
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setEmpName("john");
employee.setEmpSalary(666.66);
employeeMapper.insertEmployee(employee);
log.info("employee.getEmpId() = " + employee.getEmpId());
}
注意
Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。
-
非自增长类型主键
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!
使用
selectKey
帮助插入UUID作为字符串类型主键示例:
<insert id="insertUser" parameterType="User">
<!--先指定id属性作为主键,由UUID生成一个值被指定为随机字符串,设置到id中-->
<selectKey keyProperty="id" resultType="java.lang.String"
order="BEFORE">
SELECT UUID() as id
</selectKey>
INSERT INTO user (id, username, password)
VALUES (
#{id},
#{username},
#{password}
)
</insert>
在上例中,我们定义了一个 `insertUser` 的插入语句来将 `User` 对象插入到 `user` 表中。我们使用 `selectKey` 来查询 UUID 并设置到 `id` 字段中。
通过 `keyProperty` 属性来指定查询到的 UUID 赋值给对象中的 `id` 属性,而 `resultType` 属性指定了 UUID 的类型为 `java.lang.String`。
需要注意的是,我们将 `selectKey` 放在了插入语句的前面,这是因为 MySQL 在 `insert` 语句中只支持一个 `select` 子句,而 `selectKey` 中查询 UUID 的语句就是一个 `select` 子句,因此我们需要将其放在前面。
最后,在将 `User` 对象插入到 `user` 表中时,我们直接使用对象中的 `id` 属性来插入主键值。
使用这种方式,我们可以方便地插入 UUID 作为字符串类型主键。当然,还有其他插入方式可以使用,如使用Java代码生成UUID并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。
实体类属性和数据库字段对应关系
-
别名对应
将字段的别名设置成和实体类属性一致。
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
<!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
<!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}
</select>
> 关于实体类属性的约定:
getXxx()方法、setXxx()方法把方法名中的get或set去掉,首字母小写。
-
全局配置自动识别驼峰式命名规则
在Mybatis全局配置文件加入如下配置:
<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
<!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
SQL语句中可以不使用别名
<!-- Employee selectEmployee(Integer empId); -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>
-
使用resultMap
使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">
<!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
<!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
<id column="emp_id" property="empId"/>
<!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
<result column="emp_name" property="empName"/>
<result column="emp_salary" property="empSalary"/>
</resultMap>
<!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>
mapperXML标签总结
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。
select标签:
MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单:
<select id="selectPerson"
resultType="hashmap" resultMap="自定义结构"> SELECT * FROM PERSON WHERE ID = #{id} </select>
这个语句名为 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
注意参数符号:#{id} ${key}
MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:
// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
select 元素允许你配置很多属性来配置每条语句的行为细节:
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
insert, update 和 delete标签:
数据变更语句 insert,update 和 delete 的实现非常接近:
<insert
id="insertAuthor"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
<update
id="updateAuthor"
statementType="PREPARED"
timeout="20">
<delete
id="deleteAuthor"
statementType="PREPARED"
timeout="20">
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
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)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
三、mybatis多表映射
需要掌握:
多表查询语句使用
多表结果承接实体类设计
使用ResultMap完成多表结果映射
多表映射概念
将多表查询结果使用ResultMap标签映射到实体类对象上
下面将介绍ResultMap怎么写?
对一映射
关键词:association、javaType
association标签用于声明一个对象属性
javaType = 对象属性的类型(对象的类型)
对多映射
关键词:collection、ofType
collection:集合属性标签
ofType:集合元素数据类型
property:集合属性名(集合名)
多表映射总结
多表映射优化
setting属性 | 属性含义 | 可选值 | 默认值 |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
我们可以将autoMappingBehavior设置为full,进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签!
修改mybati-sconfig.xml:
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
修改teacherMapper.xml
<resultMap id="teacherMap" type="teacher">
<id property="tId" column="t_id" />
<!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!-- <result property="tName" column="t_name" />-->
<collection property="students" ofType="student" >
<id property="sId" column="s_id" />
<!-- <result property="sName" column="s_name" />-->
</collection>
</resultMap>
多表映射简单总结
关联关系 | 配置项关键词 | 所在配置文件和具体位置 |
---|---|---|
对一 | association标签/javaType属性/property属性 | Mapper配置文件中的resultMap标签内 |
对多 | collection标签/ofType属性/property属性 | Mapper配置文件中的resultMap标签内 |
四、Mybatis动态语句
动态语句
什么是动态语句?为什么需要?怎么解决(怎么写动态语句)
情景:在淘宝中,我们选取商品时,总会想要筛选商品,那么此时会有很多条件供我们选择,随着选择不同,为我们呈现出来的商品就不一样。每一次条件选择对应一条sql语句查询数据库,这是怎么做到的?(毕竟我们写出来的sql语句是固定的,难道我们把所有情况都列举出来?那是不可能的。。。)
下面通过mybatis框架提供的标签,可以做到写出动态语句
if和where标签
使用动态 SQL 最常见情景是根据条件包含 where / if 子句的一部分。比如:
<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="employee">
select emp_id,emp_name,emp_salary from t_emp
<!-- where标签会自动去掉“标签体内前面多余的and/or” -->
<where>
<!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
<!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
<if test="empName != null">
<!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
or emp_name=#{empName}
</if>
<if test="empSalary > 2000">
or emp_salary>#{empSalary}
</if>
<!--
第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
第二种情况:部分条件满足 WHERE emp_salary>?
第三种情况:所有条件都不满足 没有where子句
-->
</where>
</select>
总结:
if标签:每个条件对应一个if,test值表示具体的条件
where标签:包围整个条件判断。作用:1.自动加where。2.自动去掉where。3.自动去掉前面的or/and。保证sql语句不报错
set标签
<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
update t_emp
<!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
<!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
<set>
<if test="empName != null">
emp_name=#{empName},
</if>
<if test="empSalary < 3000">
emp_salary=#{empSalary},
</if>
</set>
where emp_id=#{empId}
<!--
第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
第二种情况:部分条件满足 SET emp_salary=?
第三种情况:所有条件都不满足 update t_emp where emp_id=?
没有set子句的update语句会导致SQL语法错误
-->
</update>
trim标签(了解)
使用trim标签控制条件部分两端是否包含某些字符
- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_age,emp_salary,emp_gender
from t_emp
<!-- prefix属性指定要动态添加的前缀 -->
<!-- suffix属性指定要动态添加的后缀 -->
<!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
<!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
<!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null">
emp_name=#{empName} and
</if>
<if test="empSalary > 3000">
emp_salary>#{empSalary} and
</if>
<if test="empAge <= 20">
emp_age=#{empAge} or
</if>
<if test="empGender=='male'">
emp_gender=#{empGender}
</if>
</trim>
</select>
choose/when/otherwise
在多个分支条件中,仅执行一个。
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary < 3000">emp_salary < 3000</when>
<otherwise>1=1</otherwise>
</choose>
<!--
第一种情况:第一个when满足条件 where emp_name=?
第二种情况:第二个when满足条件 where emp_salary < 3000
第三种情况:两个when都不满足 where 1=1 执行了otherwise
-->
</select>
总结:
这个其实可以等价于
if..return
else-if..return
else-if..return
else..return
foreach标签
基本用法
用批量插入举例
<!--
collection属性:要遍历的集合
item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
index属性:这里起一个名字,便于后面引用
遍历List集合,这里能够得到List集合的索引值
遍历Map集合,这里能够得到Map集合的key
-->
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
<!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
(#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach>
批量更新时需要注意
上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:
atguigu.dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true
对应的foreach标签如下:
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch">
<foreach collection="empList" item="emp" separator=";">
update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}
</foreach>
</update>
关于foreach标签的collection属性
如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:
Parameter 'empList' not found. Available parameters are [arg0, collection, list]
在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。
sql片段
抽取重复的SQL片段
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>
引用已抽取的SQL片段
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>