笔记为本人学习过程中整理的,如有错误请多多指教
目录
resultMap中的discriminator标签(鉴别器) 21
第一讲 使用MyBatis查询数据
使用MyBatis查询的经典方法(不推荐)
1.导包
mybatis-3.4.6.jar和mysql-connector-java-5.1.7-bin.jar
2.配置全局配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
3.写bean类
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", eamil="
+ email + ", gender=" + gender + "]";
}
}
4.配置SQL映射文件XXXMapper.xml(此处为EmployeeMapper.xml)
<?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="mybatis.EmployeeMapper">
<!--
namespace:名称空间
id(select标签中的,不是sql语句中的):唯一标识符
resultType:返回值类型
#{id}:意思是从传递过来的参数中取出id值
-->
<select id="selectEmp" resultType="bean.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
5.写JUnit类
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import bean.Employee;
public class MyBatisTest {
/**
* 1. 根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
* 2. 获取SqlSession实例,能直接执行已经映射的sql语句
* @throws IOException
*/
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取SqlSession实例,能直接执行已经映射的sql语句
SqlSession openSession=sqlSessionFactory.openSession();
// 第一个参数是sql的唯一标识符
// 第二个参数是执行sql要用的参数
try {
Employee employee = openSession.selectOne("mybatis.EmployeeMapper.selectEmp", 1);
System.out.println(employee);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关掉openSession
openSession.close();
}
}
}
使用MyBatis查询的接口式方法(推荐)
1.创建一个接口:
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
2.将EmployeeMapper.xml中的namespace改为接口的全类名,select的id改为接口中对应的方法名。
<mapper namespace="dao.EmployeeMapper">
<!--
namespace:名称空间
id(select标签中的,不是sql语句中的):唯一标识符
resultType:返回值类型
#{id}:意思是从传递过来的参数中取出id值
-->
<select id="getEmpById" resultType="bean.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
3.调用时过程改变:
@Test
public void test01() throws IOException{
// 1.获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
// 2.获取sqlSession对象
SqlSession openSession=sqlSessionFactory.openSession();
try {
// 3.获取接口的实现类对象
// 会为接口自动创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(employee);
} catch (Exception e) {
e.printStackTrace();
} finally {
openSession.close();
}
}
注意事项
SqlSession代表和数据库的一次会话,用完必须关闭,其与connection一样都是非线程安全的,每次使用都应该去获取新的对象,不应该定义在成员变量中
Mapper接口没有实现类,但是MyBatis会为这个接口生成一个代理对象
MyBatis的全局配置文件包含数据库连接池的信息,事务管理器信息等系统运行环境信息
Eclipse关联dtd文件
关联了mybatis-3-config.dtd与mybatis-3-mapper.dtd之后xml文件中可以代码提示了
之后全部OK即可,注意Key type是URI,mybatis-3-mapper.dtd的绑定同理
第二讲 MyBatis常用的配置标签
properties标签
MyBatis可以使用properties标签来引入外部properties配置文件的内容,resource:引入类路径下的资源,url:引入网络路径或磁盘路径下的资源
<properties resource="dbconfig.properties"></properties>
<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>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
settings标签
这是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行时行为
setting:为settings的子标签,用来设置每一个设置项
name:设置项名
value:设置项有效值
设置参数 | 描述 | 有效值 | 默认值 |
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关 | true false | TRUE |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可以通过设置fetchType属性来覆盖该项的开关状态 | true false | FALSE |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可以参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 | true false | TRUE |
defaultStatementTimeout | 设置超时时间。它决定驱动等待数据库响应的秒数 | Any positive integer | Not Set(null) |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射。即从经典数据库列名A_COLUMN到经典Java属性名aColumn的类似映射 | true false | FALSE |
更多参数设置见MyBatis手册3.1.2,不同版本的MyBatis可能所属章节不同
typeAliases标签
因为每次调用时都要写全类名十分麻烦,所以可以采用起别名的方式简化操作。但是这种方式不利于找对应的类,推荐用写全类名的方式!
typeAliases:别名处理器,可以为我们的java类型起别名
typeAlias:typeAliases的子标签,为每一个java类型起别名
type:指定要起别名的类型的全类名,默认别名为类名小写
alias:指定该全类名对应的新的别名
package:typeAliases的子标签,为某个包下的所有类批量起别名
name:指定包名,然后会自动为当前包以及下面所有的后代包的每一 个类都起一个默认别名(类名小写)
在使用package批量起别名时可能会在不同包内出现相同的类名,为避免重复别名,可以在其中一个类前使用@Alias("xxx")注解为该类取一个新的别名
别名不区分大小写,但是最好 按照大小写区分填写,提高代码可读性
MyBatis为Java的一些类指定了别名,在为自己的类指定别名时不要与这些别名冲突
Alias Mapped Type
_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标签
类型处理器,负责进行Java中的类型与数据库存储的数据类型的转换等,如把Java的String转成数据库的varchar类型等。了解即可
environments标签
MyBatis可以配置多种环境,environments标签的default属性用于指定某种环境,可以达到快速切换环境的目的,用处在于可以方便的切换开发环境与测试环境。
environment:environments的子标签,用于配置一个具体的环境信息,id代表当前环境的唯一标识符,必须有以下两个标签:
transactionManager:事务管理器
type:事务管理器的类型有JDBC | MANAGED两种取值
自定义事务管理器:实现TransactionFactory接口,type为全类名
dataSource:数据源;
type:数据源类型;UNPOOLED | POOLED | JNDI
自定义数据源:实现DataSourceFactory接口,type就是全类名
databaseIdProvider标签
支持多数据库厂商的查询,即同时支持MySQL,SQL Server,Oracle等数据库
type="DB_VENDOR":VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动:通过Connection的getMetaData()方法获取DatabaseMetaData对象,再通过该对象调用getDatabaseProductName()方法就可以返回数据库厂商标识了),MyBatis就能根据数据库厂商标识来执行不同的sql
在全局配置文件中这样配置
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
在Mapper中的select等sql标签中加入databaseId属性,属性值写给该SQL语句对应的数据库厂商名起的别名即可
<select id="getEmpById" resultType="bean.Employee" databaseId="mysql">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmpById" resultType="bean.Employee" databaseId="oracle">
select EMPLOYEE_ID id,LAST_NAME lastName,EMAIL email,GENDER gender from employees where id = #{id}
</select>
mappers标签
将SQL映射注册到全局配置中
mapper:mappers的子标签,用于注册一个sql映射
注册配置文件:
resource:引用类路径下的sql映射文件
url:引用网络路径或者磁盘路径下的sql映射文件
注册接口
class:引用(注册)接口
两种方法:
- 有sql映射文件(Mapper.xml),映射文件名必须和接口同名,并且放在与接口同一目录下
- 没有sql映射文件(Mapper.xml),所有的SQL都是利用注解写在接口上的,用法如下:
在全局配置文件中直接将接口EmployeeMapperAnnotation配置为mapper
<mapper class="dao.EmployeeMapperAnnotation" />
在EmployeeMapperAnnotation接口中这样写可以达到查询效果
public interface EmployeeMapperAnnotation {
@Select("select * from tbl_employee where id = #{id}")
public Employee getEmpById(Integer id);
}
推荐:比较重要的,复杂的Dao接口我们写入sql映射文件
不是很重要的,简单的Dao接口,为了开发快速可以使用注解
两者的配置虽不同,但是用法完全一致
package标签可以将一个包内的所有配置加入注册,接口文件放入包中即可,但是使用resource配置的需要将xxxMapper.java和xxxMapper.xml文件都放在这一目录下,这一会显得这个包里的内容十分混乱,不利于开发,我们可以在conf中创建一个相同包名的package,方便开发,且也受批量注册的影响。
注意事项
在全局配置文件中的configuration标签中配置属性时需要遵循以下顺序进行配置,不然会报错properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?
第三讲 MyBatis映射文件
映射文件指导着MyBatis如何进行数据库的增删改查,有着非常重要的意义
常用标签
标签名 | 标签作用 |
cache | 命名空间的二级缓存配置 |
cache-ref | 其他命名空间缓存配置的引用 |
resultMap | 自定义结果集映射 |
parameterMap | (已废弃)老式风格的参数映射 |
sql | 抽取可重用语句块 |
insert | 映射插入语句 |
update | 映射更新语句 |
delete | 映射删除语句 |
select | 映射查询语句 |
MyBatis允许增删改直接定义以下类型的返回值:
Integer、Long、boolean、void
MyBatis会自动进行封装
- 当使用sqlSessionFactory.openSession()方式获取openSession时数据不会自动提交,需要我们自行调用openSession.commit()方法进行手动提交;若使用sqlSessionFactory.openSession(true)方式获取openSession时,会自动提交数据,就不用手动提交了
获取自增主键
MySQL支持自增主键,自增主键值的获取,MyBatis也是利用statement.getGenreatedKeys()
在insert标签中有一个useGeneratedKeys属性,将其设置为"true"则使用自增主键获取主键值的策略;还有一个keyProperty属性,指定对应的主键属性,也就是MyBatis获取到主键值以后,将这个值封装给javaBean的哪个属性,例子如下,这样在添加操作完成后,MyBatis会将自增主键的值赋给employee的id属性
<insert id="addEmp" parameterType="bean.Employee" databaseId="mysql" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})
</insert>
Oracle不支持自增主键:Oracle使用序列来模拟自增;
每次插入的数据的主键是从序列中拿到的值,如何获取到这个值?
用selectKey标签
是通过获取序列来得到的,具体操作百度
参数处理
当Mapper接口中方法传入参数时,针对不同情况,MyBatis会进行不同处理。
单个参数:MyBatis不会做特殊处理,使用#{参数名}就可以取出参数值,但是即使#{}中写的不是参数名也能正常运行,也能达到与#{参数名}相同的效果,但是为了规范以及提升可读性,要使用#{参数名}
多个参数:例如这个方法
public Employee getEmpByIdAndLastName(Integer id, String lastName);
当方法传入多个参数时,使用#{参数名}无法获取参数值,它会被封装成一个Map,key为param1,param2,…,paramN,而value才是我们对应的传入值,要使用#{param1}...才能取出参数值
命名参数:传入多个参数时,在每个参数前加一个@Param标签,例如:
public Employee getEmpByIdAndLastName(@Param("id")Integer id, @Param("lastName")String lastName);
这样就能在写SQL语句时使用#{@Param中的参数}获取参数值了,此时key为我们在@Param中写的字符串
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入POJO
#{属性名}:取出传入的POJO的属性值
如果多个参数不是业务模型中的数据,没有对应的POJO,为了方便,我们也可以传入map
#{key}:取出map中对应的值
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
Page{
int index;
int size;
}
PS:全局配置useActualParamName(JDK1.8)时name=参数名
#{}与${}都可以用于获取map中的值或者POJO对象属性的值
区别:
#{}:是以预编译的形式,将参数设置到SQL语句中,相当于PreparedStatement,防止SQL注入
${}:取出的值直接拼装在SQL语句中,会存在安全问题
大多数情况下,我们取参数的值都是用#{}的,在原生JDBC不支持使用占位符的地方我们就可以使用${}进行取值
比如分表:按照年份表拆分用select * from ${year}_salary
因为${year}所在弟弟位置不能用?,因为${year}_salary才是一个完整的表名
#{}在进行取值的时候可以规定参数的一些规则:
参数位置支持的属性:
-javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别MyBatis对null的默认处理,比如Oracle就会报错JdbcType OTHER:无效的类型。因为MyBatis对所有的null都映射的是Jdbc的OTHER类型,Oracle不认识,需要加一个jdbcType=NULL,如#{email,jdbcType=NULL},这样当传入参数是null时就是jdbcType里的null类型,可以传入Oracle
或者在全局配置里配置jdbcTypeForNull=NULL,这样配置
第四讲 select标签详解
Select元素来定义查询操作。
id:唯一标识符
- 用来引用这条语句,需要和接口的方法名一致
parameterType:参数类型
- 可以不传,MyBatis会根据TypeHandler自动判断
resultType:返回值类型
- 别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用
resultMap:
- 外部resultMap的命名引用,和resultType属性不能同时使用
<!-- 自定义某个JavaBean的封装规则
type:自定义规则的Java类型
id:用于做区分,方便引用 -->
<resultMap type="bean.Employee" id="MyEmp">
<!-- 指定主键列的封装规则
id:定义主键属性的封装规则,虽然用result属性也不会报错,但是用id标签底层会有专门的优化
column:指定哪一列
property:指定对应的JavaBean属性 -->
<id column="id" property="id"/>
<!-- result标签用于指定普通列的封装规则 -->
<result column="last_name" property="lastName"/>
<!-- 其他未指定的列会自动进行封装,不过这样不方便检查,通常如果我们使用了resultMap,就应该把全部的映射规则都写上 -->
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee
where id=#{id}
</select>
返回List
<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
<!-- 如果返回的是一个集合,resultType要写集合中元素的类型 -->
<select id="getEmpsByLastNameLike" resultType="bean.Employee">
select * from tbl_employee
where last_name like #{lastName}
</select>
如果返回的是一个集合,resultType要写集合中元素的类型,而不是写List!
返回Map
Mapper接口中这么写
// 多条记录封装到一个map,Map<Integer, Employee>:键是这条记录的主键,值是记录封装后的JavaBean
// MapKey注解会告诉MyBatis,在封装这个map的时候用哪个属性作为主键
@MapKey("id")
public Map<Integer, Employee> getEmpsByLastNameLikeReturnMap(String lastName);
Mapper.xml中这么配置:
<select id="getEmpsByLastNameLikeReturnMap" resultType="bean.Employee">
select * from tbl_employee
where last_name like #{lastName}
</select>
联合查询
联合查询的结果如何封装到结果集?
1.使用级联属性进行封装
<!-- 联合查询的结果可以使用级联属性封装结果集 -->
<resultMap type="bean.Employee" id="MyDifEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp">
select e.id id, e.last_name last_name,e.gender gender,e.d_id d_id,
d.id did,d.dept_name dept_name
from tbl_employee e,tbl_dept d
where e.d_id-d.id and e.id=#{id}
</select>
2.使用association标签进行联合赋值
<resultMap type="bean.Employee" id="MyDifEmp2">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<!-- association 可以指定联合的JavaBean对象
property:指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略] -->
<association property="dept" javaType="bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
使用association进行分步骤查询
<!-- 使用association进行分步骤查询:
1.先按照员工id查询员工信息
2.根据查询员工信息中的d_id值去部门表查出部门信息
3.部门设置到员工中
-->
<resultMap type="bean.Employee" id="MyEmpByStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
流程:使用select指定的方法查出对象(传入column指定的这列参数的值)查出对象,并封装给property指定的属性 -->
<association property="dept"
select="dao.DepartmentMapper.getDeptById"
column="d_id"></association>
</resultMap>
- 使用分步查询的优缺点:
- 可以组合已有的方法来完成复杂的工作
- 可以使用延迟加载
Employee==>Dept
我们每次查询Employee对象的时候,都将一起查询出来
部门信息在我们使用的时候再去查询
这种功能只需在分段查询的基础上再加上两个配置
<!-- 显式的指定每个我们需要更改的配置的值,即使他是默认的 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
collection定义关联集合的封装规则
<resultMap type="bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!-- collection 定义关联集合类型的属性封装规则
ofType:指定集合里面的元素的类型
-->
<collection property="emps" ofType="bean.Employee">
<!-- 定义这个集合中元素的封装规则 -->
<id column="eid" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
<!-- public Department getDeptByIdPlus(Integer id); -->
<!-- 查询部门的时候将部门对应的所有员工信息也查询出来 -->
<select id="getDeptByIdPlus" resultMap="MyDept">
select d.id did,d.dept_name dept_name,
e.id eid,e.last_name last_name,e.email email,e.gender gender
from tbl_dept d
left join tbl_employee e on d.id=e.d_id
where d.id=#{id}
</select>
collection的分步查询也是使用select属性的,与之前的association的操作及优缺点相同。
<resultMap type="bean.Department" id="MyDeptStep">
<id column="id" property="id"/>
<result column="dept_name" property="departmentName"/>
<collection property="emps"
select="dao.EmployeeMapperPlus.getEmpsByDeptId"
column="id"
ofType="Employee"></collection>
</resultMap>
将多列的值封装到map传递,就像这么写:
column="{key1=column1, key2=column2}"
key为方法所传的参数名,column是数据库查询出的列名
- 如果全局配置了延迟加载,但是在某些地方需要立即加载,就可以在association或collection加入fetchType="eager"属性,表示立即加载,fetchType="lazy"表示延迟加载。
resultMap中的discriminator标签(鉴别器)
MyBatis可以根据鉴别器判断某列的值,来改变封装行为
<resultMap type="bean.Employee" id="MyEmpDis">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- column:指定判定的列名
JavaType:列值对应的java类型,
此处写的string是MyBatis为String起的别名 -->
<discriminator javaType="string" column="gender">
<!-- 女生 resultType:指定封装的结果类型,不能缺少,有resultMap也行 -->
<case value="0" resultType="bean.Employee">
<association property="dept"
select="dao.DepartmentMapper.getDeptById"
column="d_id"></association>
</case>
<!-- 男生 -->
<case value="1" resultType="bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="last_name" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>
第五讲 动态SQL
if标签
<!-- 查询员工,要求:携带了哪个字段查询条件就带上这个字段的值 -->
<!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
<select id="getEmpsByConditionIf" resultType="bean.Employee">
select * from tbl_employee
where 1=1
<!-- test:判断表达式(OGNL)
OGNL参照PPT或者官方文档 -->
<!-- 从参数中取值进行判断 -->
<if test="id!=null">
and id=#{id}
</if>
<!-- 可以使用&&,但是必须要用转义字符,即&&
同理""也必须用转义字符""表示 -->
<if test="lastName!=null and lastName!=""">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=""">
and email=#{email}
</if>
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
</select>
查询的时候因为后面的if判断后拼接的判断条件都要带and连接,所以必须在where后首先接上1=1,防止出错,或者使用where标签。
where标签
MyBatis会将where标签中拼装的SQL语句中多余出来的and和or去掉,但是where只会去掉第一个多出来的and或者or,所以要把and或者or写在前面
<!-- 查询员工,要求:携带了哪个字段查询条件就带上这个字段的值 -->
<!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
<select id="getEmpsByConditionIf" resultType="bean.Employee">
select * from tbl_employee
<where>
<!-- test:判断表达式(OGNL)
OGNL参照PPT或者官方文档 -->
<!-- 从参数中取值进行判断 -->
<if test="id!=null">
and id=#{id}
</if>
<!-- 可以使用&&,但是必须要用转义字符,即&&
同理""也必须用转义字符""表示 -->
<if test="lastName!=null and lastName!=""">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=""">
and email=#{email}
</if>
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
</where>
</select>
trim标签
prefix:前缀,给拼串后的整个字符串加一个前缀
prefixOverrides:前缀覆盖,去掉整个字符串前面多余的字符
suffix:后缀,给拼串后的整个字符串加一个后缀
suffixOverrides:后缀覆盖,去掉整个字符串后面多余的字符
所以上面可以换成
<trim prefix="where" suffixOverrides="and">
<!-- test:判断表达式(OGNL)
OGNL参照PPT或者官方文档 -->
<!-- 从参数中取值进行判断 -->
<if test="id!=null">
id=#{id} and
</if>
<!-- 可以使用&&,但是必须要用转义字符,即&&
同理""也必须用转义字符""表示 -->
<if test="lastName!=null and lastName!=""">
last_name like #{lastName} and
</if>
<if test="email!=null and email.trim()!=""">
email=#{email} and
</if>
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</trim>
choose标签
分支选择:相当于switch-case,含子标签when, otherwise
<!-- 如果带了id就用id查,如果带了lastName就用lastName查 -->
<select id="getEmpsByConditionChoose" resultType="bean.Employee">
select * from tbl_employee
<where>
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="lastName!=null">
last_name like #{lastName}
</when>
<otherwise></otherwise>
</choose>
</where>
</select>
update子标签
此处我们需要更新员工信息,只需要更新我们的指定项即可
<!-- public void updateEmp(Employee employee); -->
<update id="updateEmp">
update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id}
</update>
- 注意:set中的if中写的设置语句后的逗号","不能省略,不然就无法一次更新多个信息了
想达到此效果也可用trim标签完成,只需要将原来的set处的标签改为:
<trim prefix="set" suffixOverrides=","></trim>
foreach标签
有时我们的选择语句中会用到in作为判断依据
如:
select * from tbl_employee where id in (1,2,3)
此时我们可以用foreach标签优化此处1,2,3的显示。
- collection:指定要遍历的集合
- list类型的参数会特殊处理封装在map中,map的key就叫list
- item:将当前遍历出的元素赋值给指定的变量
- separator:表示每个元素与每个元素之间的分隔符
- open:为遍历出的结果拼接一个开始的字符
- close:为遍历出的结果拼接一个结束的字符
- index:索引。遍历list的时候index表示的就是索引,item就是map的值;遍历map的时候index表示的就是map的key,item就是map的值
接着在标签内就能用#{变量名(item的值)}取出变量的值,也就是当前遍历出的元素了
设ids是一个集合里面有{1,2,3}三个元素,这样上面的SQL配置就可以改成如下这样:
select * from tbl_employee where id in
<foreach collection="ids" item="item_id" separator="," open="(" close=")">
#{item_id}
</foreach>
也可以将 where id in 也一并写入open标签中
Insert的使用
MySQL当一次要插入多条数据时可以这样写sql语句
insert into tbl_employee(last_ name,email,gender,d_id)
values('tom','tom@qq.com','0',1),('frank','frank@qq.com','1',2)
此时红字部分就可以用foreach标签来简单描述
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
或者在dbconfig.properties配置文件中写上
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
然后将整个SQL(不仅仅局限于insert语句,update,delete语句也可以)放入foreach中,在separator属性中赋值为分号";"即可
就相当于一次执行多个SQL语句
Oracle不支持,需要在前面加上begin,尾部加上end
MyBatis的内置参数
代表整个参数
- 单个参数:_parameter就是这个参数
- 多个参数:参数会被封装为一个map:_parameter就是代表这个map
如果在全局配置文件mybatis-config.xml中配置了databaseIdProvider标签,那么_databaseId就是代表当前数据库的别名
在不同的数据库会有不同的操作时,我们可以用if标签区分,
<if test="_databaseId=='mysql'">......</if>
<if test="_databaseId=='oracle'">......</if>
bind标签
不推荐!
在增删改查标签内还有一个bind子标签,是绑定的意思
作用:可以将OGNL表达式绑定到一个变量中,方便后来引用这个变量的值
<bind name="_lastName" value="'%'+lastName+'%'">
这样就相当于在原先的lastName的值的两端加上了%,方便使用like查询,这样在接下来写就可以写like #{_lastName}了,在Java的方法中也不用再每次都手动加%了
不推荐使用这种方式,要是模糊查询还是每次在Java方法中自己加%、_等符号比较好,这样查询的操作自由度要更高,规则有变的情况下我们不需要更改我们的配置值
sql标签与include标签
与增删改查标签同级,我们可以将使用频率比较高的sql语句部分写入sql标签中
如
<sql id="insertColumn">
employee_id,last_name,email
</sql>
这样在写插入语句时就可以这样:
insert into employees(
<include refid="insertColumn"></include>
)
当数据库类型不同,导致需要保存的东西不同时,可以在sql标签内写<if>子标签判断_databaseId即可
在<include>标签内还可以用<property name="xxx" value="aaa">标签自定义一些属性,在<sql>标签中可以用${xxx(name属性值)}来调用,注意是$不是#
第六讲 MyBatis缓存
MyBatis系统中默认定义了两级缓存
一级缓存和二级缓存
- 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
一级缓存也称为本地缓存,它是一直开启的
与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
从缓存中获取的对象地址是相等的
- SqlSession不同,如果同一个查询用的是两个不同的SqlSession对象,那么就会向数据库重新发送SQL语句进行查询
- SqlSession相同,但是查询条件不同(一级缓存中没有这个数据)
- SqlSession相同,但是两次查询之间执行了增删改操作(可能使数据库的数据产生变化,需要重新查询)
- SqlSession相同,手动清除了缓存(调用了openSession.clearCache();方法将缓存清空)
二级缓存
二级缓存也称为全局缓存,是基于namespace级别的缓存,一个namespace对应一个二级缓存
工作机制
- 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果会话关闭,一级缓存中的数据才会被保存到二级缓存中;新的会话查询信息就可以参照二级缓存中的内容
- sqlSession用EmployeeMapper查询的Employee和用DepartmentMapper查询的Department放在不同的namespace中,不同namespace查出的数据放在自己对应的缓存(map)中
- 开启全局二级缓存配置(此设置不影响一级缓存,设为false也不会关闭一级缓存)
在settings中配置
<setting name="cacheEnabled" value="true"/>
即可开启全局二级缓存配置(默认开启,保险起见再配置下)
- 去mapper.xml中配置使用二级缓存
配置<cache></cache>标签即可
cache中的属性含义
- eviction:缓存的回收策略(默认是LRU)
- LRU:最近最少使用的,即移除最长时间不被使用的对象
- FIFO:先进先出,即按照对象进入缓存的顺序来移除它们
- SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用,更积极地移除基于垃圾回收器状态和弱引用规则的对象
- flushInterval:缓存刷新间隔,即缓存多长时间清空一次,可以自行设置,单位是毫秒(默认不清空)
- readOnly:是否只读(默认false)
- true:只读,这样MyBatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据,MyBatis为了加快获取速度,会直接将数据在缓存中的引用交给用户。不安全,速度快
- false:非只读,MyBatis认为获取的数据可能会被修改,MyBatis会利用序列化&反序列化技术克隆一份相同的数据。安全,速度慢
- size:指定缓存中存放多少元素
- type:指定自定义缓存的全类名,实现Cache接口就可以使用
- 我们的POJO需要实现序列化接口Serializable
缓存相关设置/属性
- <setting name="cacheEnabled" value="true"/>设为"false"时二级缓存无法使用,但是不会关闭一级缓存
- 每个select标签都有useCache属性,一般来说默认为"true",改为"false"时二级缓存不使用,一级缓存依旧可以使用
- 每个增删改标签都有flushCache属性,默认为"true",所以增删改执行完成之后就会清除缓存,一级二级都会清除;查询标签其实也有,只是默认值是"false",可以自行修改为"true",这样每次查询之前就都会清除缓存了,相当于不使用缓存
- sqlSession.clearCache()方法只是清除当前session的一级缓存,不影响二级缓存
- localCacheScope:本地缓存作用域(一级缓存SESSION),当前会话的所有数据保存在会话缓存中
STATEMENT:可以禁用一级缓存
- 缓存的查询顺序:
- 二级缓存
- 一级缓存
- 数据库
第七讲 MyBatis整合Spring
除了需要MyBatis和Spring本身需要的jar包以外,还需要一个MyBatis与Spring连接的包mybatis-spring-1.3.0.jar,MyBatis本身的全局配置文件mybatis-config.xml和数据库连接的配置文件dbconfig.properties文件也都不能少
Spring和SpringMVC的配置内容都写在web.xml中,配置方式与正常配置方式相同