- MyBatis
- 1.核心配置文件mybatis-config.xml
- 1.4typeAliases
- 1.5配置的顺序
- 1.6测试的结果
- 2.测试CRUD(增删改查)
- 2.2通过package管理映射文件的引入
- 2.3MyBatis查询的三种方式
- 2.4MyBatis获取参数值的两种方式
- {}:preparedStatement(预编译,使用通配符操作SQL语句)
- 3.传递参数的方式
- 3.2当传输参数为JavaBean时:
- 3.3当传输多个参数时:
- 3.4当传输Map参数时:
- 3.5命名参数(建议使用)
- 3.6传输Collection/Array
- 4.自定义映射关系
- 4.2多对一映射(association的简单使用)少用
- 4.3多对一映射(association分布查询)多用
- 4.4分步查询的延迟加载
- 4.5一对多映射自定义映射
- 4.6一对多的分步查询
- 4.7延迟加载2.0
- 5.Mybatis动态SQL
- 5.3trim
- 5.4choose(when、otherwise)
- 5.5foreach
- 5.6批量操作
- 5.7sql标签
- 6.Mybatis的缓存
- 6.4缓存的相关属性设置
- 6.5整合第三方缓存
MyBatis
什么是MyBatis?
1.是支持定制化 SQL、存储过程以及高级映射的**持久层框架**。
**定制化SQL**:可以自己写SQL 语句。
**存储过程**:使用SQL语句写的一段代码。(就是SQL 里的存储过程)
**普通映射**:自动形成**最终查询出来字段名和属性名的映射关系**。(完全自动映射)
**高级映射**:自动映射 多对多、多对一、一对多。
2.几乎**避免了所有的JDBC代码和手动设置参数,以及获取结果集**。
它底层就是使用的JDBC。
MyBatis有自动设置参数的方式。${} 和 #{}
#{} :是通配符 :?。里面写形参。 uid=#{uid}
![](https://g.yuque.com/gr/latex?%7B%7D%20%EF%BC%9A%E6%98%AF%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%8B%BC%E6%8E%A5%E7%9A%84%E6%96%B9%E5%BC%8F%E3%80%82%EF%BC%88%E7%94%A8%E4%BA%8E%E6%A8%A1%E7%B3%8A%E6%9F%A5%E8%AF%A2%E5%92%8C%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4%EF%BC%89%E3%80%82%E9%87%8C%E9%9D%A2%E5%BF%85%E9%A1%BB%E5%86%99value%EF%BC%9Auid%3D#card=math&code=%7B%7D%20%EF%BC%9A%E6%98%AF%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%8B%BC%E6%8E%A5%E7%9A%84%E6%96%B9%E5%BC%8F%E3%80%82%EF%BC%88%E7%94%A8%E4%BA%8E%E6%A8%A1%E7%B3%8A%E6%9F%A5%E8%AF%A2%E5%92%8C%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4%EF%BC%89%E3%80%82%E9%87%8C%E9%9D%A2%E5%BF%85%E9%A1%BB%E5%86%99value%EF%BC%9Auid%3D){value}
普通查询时会获得结果集,然后需要我们去操作结果集进行遍历等等,**MyBatis避免了获取结果集合,它会直接给我们返回值(单个值、一个对象、一个list集合)**。
3.可以**使用简单的XML或注解用于配置和原始映射**。将接口和java的 pojo 类映射成数据库中的记录。
可以使用 注解或XML 进行配置。(在 MyBatis 里一般不使用注解)
4.是一个半自动的ORM(**对象关系映射 Object Relation Mapping**) 框架(将接口和java的 pojo 映射成数据库中的记录)。
Relation:关系。(关系型数据库中的一条记录)
Object:Java中的对象。
我们只需要操作实体对象,就可以映射成对数据库中记录的操作。
持久化技术的对比
1.JDBC
执行效率最高(原生的代码,不需要解析任何东西)。但是开发效率低(所有东西都需要自己编写)。
2.Hibernate和JAP
长难复杂SQL。全自动生成SQL,不能对SQL进行优化。
全自动映射框架。大量字段的POJO进行部分映射时比较困难,性能下降。
3.MyBatis
开发效率比JDBC高。
SQL可以自己进行优化。
MyBatis搭建过程
1.导入jar 包:mybatis.jar(mybatis的jar包) 、
log4j.jar(日志,同时也要导入它的配置文件log4j.xml。配置文件定义了日志数组格式)
mysql-connector-java.jar(数据库连接的jar包)
2.创建 mybatis 的核心(全局)配置文件 mybatis-config.xml,并配置。(核心文件写:如何连接数据库)
<?xml version="1.0" encoding="UTF-8" ?>
<!--作用和命名空间一样。DOCTYPE 后的 单词 就是这个xml文件的根标签-->
<!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"/>
<!--dataSourc:数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql//localhost:3306/ssm"/>
<property name="username" value="root"/>
<property name="password" value="3306"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
3.创建映射文件 xxxMapper.xml ,并配置。(映射文件:如何操作数据库)
UserMapper.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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.UserMapper">
<!--
<select>:定义查询语句
id:设置SQL语句的唯一标识,完成与方法的绑定(需要与方法名保持一致)。
resultType:结果类型。
-->
<select id="getUserById" resultType="com.guolian.bean.User">
select uid,user_name userName,password,age,sex from user where uid = #{uid}
</select>
</mapper>
4.创建Mapper 接口,需要实现两个绑定。
/**
* 创建Mapper接口,实现两个绑定
* 一:接口的全限定名要和映射文件的 namespace 保持一致
* 二:接口中的方法名要和SQL语句的id保持一致
*/
public interface UserMapper {
User getUserById(String uid);
}
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.UserMapper">
<!--
<select>:定义查询语句
id:设置SQL语句的唯一标识,完成与方法的绑定(需要与方法名保持一致)。
resultType:结果类型。
-->
<select id="getUserById" resultType="com.guolian.bean.User">
select uid,user_name,password,age,sex from user where uid = #{uid}
</select>
</mapper>
5.获取MyBatis 操作数据库的会话对象。(SqlSession)。通过getMapper()获取接口的动态代理实现类。
@org.junit.Test
public void getUserById() throws IOException {
//获取sqlSession的过程
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = build.openSession();
//测试sqlsession是否可以操作数据库
//getMapper():会通过动态代理,动态生成对应的代理实现类。
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.getClass().getName());
User userById = mapper.getUserById("1");
System.out.println(userById);
}
6.测试。
1.核心配置文件mybatis-config.xml
properties 设置或引入资源文件
settings 定义全局设置
typeAliases 定义类型别名
typeHandlers 定义类型的处理
objectFactory 定义类型工厂
plugins 定义插件、组件
environments 定义环境
datebaseIdProvider 定义数据库产品标识(mysql、Oracle等等)。因为不同的数据库有一些不同的语法。
mappers 引入(注册)映射文件
1.1environments
<!--
<environments>:设置连接数据库的环境,可以定义多个
default:设置默认使用的数据库环境。
-->
<environments default="mysql">
<!--
<environment>:设置某个具体的数据库环境
id:数据库环境的唯一标识
-->
<environment id="mysql">
<!--
<transactionManager>:事务管理
type有两个值:JDBC、MANAGED
JDBC:使用JDBC原生的事务管理方式
MANAGED:谁能管理就交给谁管理。
-->
<transactionManager type="JDBC"/>
<!--
dataSourc:数据源
type:POOLED、UNPOOLED、JNDI
POOLED:使用数据库连接池
UNPOOLED:不使用数据库连接池
JNDI:调用上下文中的数据源
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
<property name="username" value="root"/>
<property name="password" value="3306"/>
</dataSource>
</environment>
<environment id="Oracle">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
<property name="username" value="root"/>
<property name="password" value="3306"/>
</dataSource>
</environment>
</environments>
1.2properties
<configuration>
<!--
<properties>:用来设置或引入资源文件
resource:在类路径下进行访问
url:在网络路径或磁盘路径下进行访问
-->
<properties resource="jdbc.properties"></properties>
<!--也可以像这样直接设置
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"></property>
</properties>
-->
<environments default="mysql">
<environment id="mysql">
<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> :引入映射文件-->
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
1.3settings
还有一些setting的参数和参数值可以在MyBatis相关文档里查询。
这里就可以将映射文件里,SQL语句里设置的别名去掉。
<settings>
<!--
mapUnderscoreToCamelCase:把下划线转化为驼峰
user_name->userNmae
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1.3.2映射文件
<select id="getUserById" resultType="com.guolian.bean.User">
select uid,user_name,password,age,sex from user where uid = #{uid}
</select>
1.4typeAliases
这个设置是在核心文件里设置,但是可以在映射文件里使用。
<typeAliases>
<!--
为类型设置类型别名
type:写Java类型,若只设置type,默认的别名就是类名且不区分大小写
alias:手动设置的别名
-->
<typeAlias type="com.guolian.bean.User" alias="u"></typeAlias>
<!--
包下的所有类都会创建别名。
默认的别名就是类名且不区分大小写
-->
<package name="com.guolian.bean"></package>
</typeAliases>
1.4.2映射文件:
<select id="getUserById" resultType="u">
select uid,user_name,password,age,sex from user where uid = #{uid}
</select>
1.5配置的顺序
这些标签的配置都有固定的顺序,如果顺序出错:configuration标签将会报错。
可以按照错误信息里的提示,更改标签的顺序。
1.6测试的结果
第一句:SQL语句
第二句:传入的参数
第三句:返回的结果数
第四句:返回的结果
2.测试CRUD(增删改查)
2.1简单测试CRUD
手动处理事务:
SqlSession sqlSession = build.openSession();
需要使用 :sqlSession.comit(); 来手动的提交事务
自动处理事务:
SqlSession sqlSession = build.openSession(true);
每次语句完成后就自动提交。
只要更改接口里方法的返回值类型,MyBatis就会自动匹配相应的返回值。
比如说:增删改:可以返回int类型(int、integer)、可以不返回(void)、可以返回boolean类型(boolean)
2.1.1配置映射文件EmpMapper.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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.empMapper">
<!-- public Emp getEmpByEid(String eid);-->
<select id="getEmpByEid" resultType="Emp">
SELECT eid,ename,age,sex FROM emp WHERE eid = #{eid}
</select>
<!--public List<Emp> getAllEmp();-->
<!--方法的返回值是list,resultMap不一定必须要list-->
<!--Mybatis会自动的根据我们方法中设置的返回值。放在相对应的容器中-->
<select id="getAllEmp" resultType="Emp">
SELECT eid,ename,age,sex FROM emp
</select>
<!--public void addEmp(Emp emp);-->
<insert id="addEmp">
INSERT INTO emp values(null,#{ename},#{age},#{sex})
</insert>
<!--public void updateEmp(Emp emp);-->
<!--parameterType:限定传入的参数类型-->
<update id="updateEmp" parameterType="com.guolian.bean.Emp">
UPDATE emp set ename = #{ename},age = #{age},sex = #{sex} WHERE eid = #{eid}
</update>
<!--public void deleteEmp(String eid);-->
<delete id="deleteEmp">
DELETE FROM emp WHERE eid = #{eid}
</delete>
</mapper>
2.1.2接口:empMapper.java
public interface empMapper {
//根据eid查询一个信息
public Emp getEmpByEid(String eid);
//获取所有员工信息
public List<Emp> getAllEmp();
//添加员工信息
public void addEmp(Emp emp);
//修改员工信息
public void updateEmp(Emp emp);
//删除员工信息
public void deleteEmp(String eid);
}
2.1.3测试:empMapperTest.java
public class empMapperTest {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//SqlSession sqlSession = build.openSession();//需要手动处理事务
SqlSession sqlSession = build.openSession(true);//自动处理事务
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
public empMapperTest() throws IOException {
}
@org.junit.Test
public void getEmpByEid() throws IOException {
//根据eid获取员工信息
Emp empByEid = mapper.getEmpByEid("3");
System.out.println(empByEid);
}
@org.junit.Test
public void getAllEmp() {
List<Emp> allEmp = mapper.getAllEmp();
System.out.println(allEmp);
}
@org.junit.Test
public void addEmp() {
mapper.addEmp(new Emp(null,"王军",23,"男"));
//如果获取sqlsession时,设置的是手动处理事务(build.openSession())。则需要手动提交。
//sqlSession.commit();//提交事务
}
@org.junit.Test
public void updateEmp() {
mapper.updateEmp(new Emp(2,"卢本伟",20,"男"));
}
@org.junit.Test
public void deleteEmp() {
mapper.deleteEmp("10");
}
}
2.2通过package管理映射文件的引入
<!--<mappers> :引入映射文件-->
<mappers>
<!--
<mapper resource="EmpMapper.xml"/>
<mapper resource="DeptMapper.xml"/>
-->
<!--这种写法,要求Mapper接口和Mapper映射文件必须在同一包下-->
<package name="com.guolian.mapper"></package>
</mappers>
虽然映射文件是写在conf文件夹下,但是整个项目映射到服务器端之后。这两个相同类名的包合并了。所以可行。
2.3MyBatis查询的三种方式
当以Map集合的形式存储多个值时。需要设置相对应的Key。
以Map集合存储单个值时,不需要key。
2.3.1EmpSelectMapper.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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.EmpSelectMapper">
<!--Emp getEmpByEid(String eid);-->
<select id="getEmpByEid" resultType="com.guolian.bean.Emp">
SELECT * FROM emp WHERE eid = #{eid}
</select>
<!--Integer getCount();-->
<select id="getCount" resultType="Integer">
SELECT COUNT(eid) FROM emp
</select>
<!-- Map<String,Object> getEmpMapByEid(String eid);-->
<!--将单条查询信息放入Map集合中,以字段名为键以字段值为值-->
<select id="getEmpMapByEid" resultType="hashmap">
SELECT * FROM emp WHERE eid = #{eid}
</select>
<!--Map<String,Object> getAllEmpMap();-->
<!--将多条查询信息放入Map集合中,需要在接口的方法上加上注解@MapKey("")。为Map集合设置键-->
<select id="getAllEmpMap" resultType="hashmap">
SELECT * FROM emp
</select>
</mapper>
2.3.2EmpSelectMapper.java
public interface EmpSelectMapper {
//根据eid查询一个员工信息
Emp getEmpByEid(String eid);
//查询所有员工数量
Integer getCount();
//以map集合获取一个员工信息
Map<String,Object> getEmpMapByEid(String eid);
//以map集合获取所有员工信息
@MapKey("eid")
Map<String,Object> getAllEmpMap();
}
2.3.3EmpSelectMapperTest.java
public class EmpSelectMapperTest {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
EmpSelectMapper mapper = sqlSession.getMapper(EmpSelectMapper.class);
public EmpSelectMapperTest() throws IOException {
}
@Test
public void getEmpByEid() throws IOException {
Emp empByEid = mapper.getEmpByEid("1");
System.out.println(empByEid);
}
@Test
public void getCount() {
Integer count = mapper.getCount();
System.out.println(count);
}
@Test
public void getEmpMapByEid() {
Map<String, Object> empMapByEid = mapper.getEmpMapByEid("1");
System.out.println(empMapByEid);
}
@Test
public void getAllEmpMap() {
Map<String, Object> allEmpMap = mapper.getAllEmpMap();
System.out.println(allEmpMap);
}
}
2.4MyBatis获取参数值的两种方式
在使用批量操作和模糊查询时,使用:${}
其它时候,都使用:#{}
2.4.1${}
${}:Statement(使用字符串拼接操作SQL语句)
ParamMapper.xml:
一定要注意单引号问题
<insert id="insert02">
INSERT INTO emp VALUES(null,'${ename}',${age},'${sex}')
</insert>
控制台输出的SQL语句:
NSERT INTO emp VALUES(null,'伊卡洛斯',10000,'女')
2.4.2#{}
{}:preparedStatement(预编译,使用通配符操作SQL语句)
ParamMapper.xml:
<insert id="insert">
INSERT INTO emp VALUES(null,#{ename},#{age},#{sex})
</insert>
控制台输出的SQL语句:
INSERT INTO emp VALUES(null,?,?,?)
2.5获取自动生成的主键
2.5.1配置映射文件
useGeneratedKeys:是否可以使用自动生成的主键<br />
keyProperty:将自动生成的主键**赋值给传递过来的参数的哪一个属性**。
<?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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.ParamMapper">
<!--void insert(Emp emp);-->
<!--
useGeneratedKeys:可以使用自动生成的主键
keyProperty:将自动生成的主键赋值给传递过来的参数的哪一个属性
这里将值赋给了传递过来的Emp对象的eid属性。
-->
<insert id="insert" useGeneratedKeys="true" keyProperty="eid">
INSERT INTO emp VALUES(null,#{ename},#{age},#{sex})
</insert>
</mapper>
2.5.2测试
@Test
public void insert() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
ParamMapper mapper = sqlSession.getMapper(ParamMapper.class);
Emp emp = new Emp(null, "伊卡洛斯", 10000, "女");
mapper.insert(emp);
System.out.println(emp.getEid());
}
2.5.3结果
3.传递参数的方式
3.1当传输参数为单个String或基本数据属性和其包装类:
3.1.1#{}:可以以任意的名字获取参数值
<select id="getEmpByEid" resultType="com.guolian.bean.Emp">
SELECT eid,ename,age,sex FROM emp WHERE eid = #{abcd}
</select>
3.1.2
{value}或${_parameter}获取
<select id="getEmpByEid02" resultType="com.guolian.bean.Emp">
SELECT eid,ename,age,sex FROM emp WHERE eid = ${value}
</select>
3.2当传输参数为JavaBean时:
#{}和![](https://g.yuque.com/gr/latex?%7B%7D**%E9%83%BD%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E5%B1%9E%E6%80%A7%E5%90%8D%E7%9B%B4%E6%8E%A5%E8%8E%B7%E5%8F%96%E5%B1%9E%E6%80%A7%E5%80%BC**%E3%80%82%E4%BD%86%E6%98%AF**%E8%A6%81%E6%B3%A8%E6%84%8F#card=math&code=%7B%7D%2A%2A%E9%83%BD%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E5%B1%9E%E6%80%A7%E5%90%8D%E7%9B%B4%E6%8E%A5%E8%8E%B7%E5%8F%96%E5%B1%9E%E6%80%A7%E5%80%BC%2A%2A%E3%80%82%E4%BD%86%E6%98%AF%2A%2A%E8%A6%81%E6%B3%A8%E6%84%8F){}的单引号问题**。
<!--
useGeneratedKeys:可以使用自动生成的主键
keyProperty:将自动生成的主键赋值给传递过来的参数的哪一个属性
这里将数据库自动生成的主键值赋值给了传递过来的emp对象的eid属性。
-->
<insert id="insert" useGeneratedKeys="true" keyProperty="eid">
INSERT INTO emp VALUES(null,#{ename},#{age},#{sex})
</insert>
<insert id="insert02">
INSERT INTO emp VALUES(null,'${ename}',${age},'${sex}')
</insert>
3.3当传输多个参数时:
3.3.1#{}:可以通过在#{}里写0、1、2或者param1、param2、param3来获取。
<select id="getEmpByEidAndEname" resultType="com.guolian.bean.Emp">
<!--SELECT eid,ename,age,sex FROM emp WHERE eid = #{param1} AND ename = #{param2}-->
SELECT eid,ename,age,sex FROM emp WHERE eid = #{0} AND ename = #{1}
</select>
3.3.2
{}里写param1、param2、param3**来获取
<select id="getEmpByEidAndEname" resultType="com.guolian.bean.Emp">
SELECT eid,ename,age,sex FROM emp WHERE eid = ${param1} AND ename = '${param2}'
</select>
**因为当传输多个参数时,mybatis会默认将这些参数放在map集合中**
两种方式:
1、键为:0、1、2、3.....n-1,以参数为值
2、键为:param1、param2、param3...paramN,以参数为值
3.4当传输Map参数时:
**#{}和${}都可以通过键的名字直接获取值**。但是注意单引号问题。
<select id="getEmpByMap" resultType="com.guolian.bean.Emp">
SELECT eid,ename,age,sex FROM emp WHERE eid = ${eid} AND ename = '${ename}'
</select>
public void getEmpByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("eid","1");
map.put("ename","张三");
Emp empByEid = mapper.getEmpByMap(map);
System.out.println(empByEid);
}
3.5命名参数(建议使用)
也是将参数放入Map集合中
可以通过 @Param(“”) 为Map集合指定键的名字
//直接将参数放在Map集合中,键就是 @Param("")里定义的
Emp getEmpByEidAndEnameByParam(@Param("eid") String eid, @Param("ename") String ename);
**#{}和${}都可以通过键的名字直接获取值**。但是注意单引号问题。
3.6传输Collection/Array
Mybatis会将其放入Map集合中。
List以list为键,Array以array为键。
4.自定义映射关系
resultType自动映射
resultMap自定义映射
id:用于处理主键的映射
result:用于处理非主键的映射
association:用来处理一对一和多对一的映射关系
collection:用来处理一对多和多对多的映射关系
4.1多对一映射(只用id、result配置)少用
4.1.1映射文件
<?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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.EmpDeptMapper">
<!--自定义映射关系-->
<resultMap id="EmpResultMap" type="com.guolian.bean.Emp">
<!--
<id>定义主键的映射关系。
column:查询出来的列名
property:需要对应的属性名
-->
<id column="eid" property="eid"></id>
<!--
<result>定义非主键属性的映射关系
-->
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<result column="did" property="dept.did"></result>
<result column="dname" property="dept.dname"></result>
</resultMap>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="EmpResultMap">
SELECT e.eid,e.ename,e.age,e.sex,e.did,d.dname FROM emp e LEFT JOIN dept d ON e.did = d.did
</select>
</mapper>
4.1.2测试
public class EmpDeptMapperTest {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//SqlSession sqlSession = build.openSession();//需要手动处理事务
SqlSession sqlSession = build.openSession(true);//自动处理事务
EmpDeptMapper mapper = sqlSession.getMapper(EmpDeptMapper.class);
public EmpDeptMapperTest() throws IOException {
}
@Test
public void getAllEmp() {
List<Emp> allEmp = mapper.getAllEmp();
System.out.println(allEmp);
}
}
4.1.3执行结果
4.2多对一映射(association的简单使用)少用
4.2.1映射文件
MyBatis会创建一个JavaType里的属性的对象,然后将查询出来的字段值赋值给对象的属性,再将它创建的对象赋值给 我们创建的属性
<?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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.EmpDeptMapper">
<resultMap id="EmpResultMap" type="com.guolian.bean.Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<!--MyBatis会创建一个JavaType里的属性的对象,然后将查询出来的字段值赋值给
对象的属性,再将它创建的对象赋值给 我们创建的属性-->
<association property="dept" javaType="com.guolian.bean.Dept">
<id column="did" property="did"></id>
<result column="dname" property="dname"></result>
</association>
</resultMap>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="EmpResultMap">
SELECT e.eid,e.ename,e.age,e.sex,e.did,d.dname FROM emp e LEFT JOIN dept d ON e.did = d.did
</select>
</mapper>
4.3多对一映射(association分布查询)多用
4.3.1配置文件
<?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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.EmpDeptMapper">
<resultMap id="empMapslet" type="com.guolian.bean.Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<!--
property:需要处理的属性
select:分步查询的SQL的id。即接口的全限定名.方法名
column:分步查询的条件,注意:此条件必须是从数据库查询过的(上一步查询过的)
当column中要传输多个值时:column本身就是把值封装成了Map集合,所以只需要这样写:column="{did=did,dname=dname}"。前面一个did,dname是键,后面的是查询出来的值。
-->
<association property="dept" select="com.guolian.mapper.DeptMapper.getDeptByDid" column="did">
</association>
</resultMap>
<!--Emp getEmpSlep(String eid);-->
<select id="getEmpSlep" resultMap="empMapslet">
SELECT eid,ename,age,sex,did FROM emp WHERE eid = #{eid}
</select>
</mapper>
分布查询用到的SQL语句:
<?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">
<!--映射文件-->
<!--namespace:命名空间,绑定住相应的Mapper接口-->
<mapper namespace="com.guolian.mapper.DeptMapper">
<!--Dept getDeptByDid(String did);-->
<select id="getDeptByDid" resultType="com.guolian.bean.Dept">
SELECT * FROM dept WHERE did = #{did}
</select>
</mapper>
4.3.2结果
根据控制台输出的结果可知:
1.先通过eid查询出emp表的员工信息
2.再通过emp表中查询出来的did查询对应的部门信息。
4.4分步查询的延迟加载
如果我只用emp中信息,那就先查询emp。直到我用到了dept中的信息,再去查询dept。
延迟加载需要在核心配置文件里配置(mybatis-config.xml)。在settings标签里配置。
lazyLoadingEnabled(是否开启延迟加载)和aggressiveLazyLoading(是否加载全部SQL语句)必须成对使用:
**当lazyLoadingEnabled的value值为false时,aggressiveLazyLoading的value必须为true**。
**当lazyLoadingEnabled的value值为true时,aggressiveLazyLoading的value必须为false**。
<settings>
<!--
mapUnderscoreToCamelCase:把下划线转化为驼峰
user_name->userNmae
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"></setting>
<!--是否查询所有数据-->
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
4.5一对多映射自定义映射
4.5.1配置文件
<resultMap id="deptMap" type="com.guolian.bean.Dept">
<id column="did" property="did"></id>
<result column="dname" property="dname"></result>
<!--
<collection>:处理一对多和多对多的关系
ofType:集合中的类型
-->
<collection property="emps" ofType="com.guolian.bean.Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
</collection>
</resultMap>
<!-- Dept getDeptEmpByDid(String did);-->
<select id="getDeptEmpByDid" resultMap="deptMap">
SELECT d.did,d.dname,e.eid,e.ename,e.age,e.sex FROM dept d LEFT JOIN emp e on d.did = e.did WHERE d.did = #{did}
</select>
4.6一对多的分步查询
一对多的分步查询和多对一的分步查询一样。
只要之前配置了延迟加载,那么,只要是分步查询,就会启用延迟加载。无论是多对一还是一对多。
4.6.1配置文件
<!--一对多分步查询-->
<resultMap id="deptMapStep" type="Dept">
<id column="did" property="did"></id>
<result column="dname" property="dname"></result>
<collection property="emps" select="com.guolian.mapper.EmpDeptMapper.getEmpListByDid" column="did">
</collection>
</resultMap>
<!--Dept getOnlyDeptByDid(String did);-->
<select id="getOnlyDeptByDid" resultMap="deptMapStep">
SELECT did,dname FROM dept WHERE did = #{did}
</select>
<!--List<Emp> getEmpListByDid(String did);-->
<select id="getEmpListByDid" resultType="com.guolian.bean.Emp">
select eid,ename,age,sex from emp WHERE did = #{did};
</select>
4.7延迟加载2.0
如果按照之前的,在核心配置文件里启用延迟加载就会让所有的分步查询都启用延迟加载。
当我们需要排除一些延迟加载,或者只需要用几个延迟加载时。就可以使用:fethcType
<collection property="emps" select="com.guolian.mapper.EmpDeptMapper.getEmpListByDid" column="did" fetchType="eager">
</collection>
fetchType=”eager”。排除当前分步查询的延迟加载。
fetchType=”lazy”。开启当前分步查询的延迟加载。
5.Mybatis动态SQL
5.1Mybatis动态SQL简介
1.极大的简化了我们拼装SQL的操作
2.动态SQL其实就是一系列的标签
5.2if where
<!--
<if test=""></if>:通过test表达式,拼接SQL。
<where></where>:添加 where 关键字,并去掉开头多余的and关键字
-->
适用于多条件查询,如果有当前条件,则加入SQL语句中。如果没有当前条件,则不加入到SQL语句中。
5.2.2 mapper.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="com.guolian.mapper.EmpMapper">
<!--List<Emp> getEmpByMore(Emp emp);-->
<select id="getEmpByMore" resultType="com.guolian.bean.Emp">
SELECT * FROM emp
<where>
<if test="eid != null">
eid=#{eid}
</if>
<if test="ename !=null and ename != ''">
AND ename=#{ename}
</if>
<if test="age != null">
AND age=#{age}
</if>
<if test="sex == 1 or sex == 0">
AND sex=#{sex}
</if>
</where>
</select>
</mapper>
如果不加where 标签,并且没有输入eid条件,SQL语句中就会多出一个and。并报错。
在where的后面多出了一个AND 。
5.2.1test.java
@Test
public void testIf() throws IOException {
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setSex("1");
List<Emp> empByMore = mapper.getEmpByMore(emp);
for (Emp p : empByMore){
System.out.println(p);
}
}
5.3trim
<trim prefix="" suffix="" prefixOverrides="" suffixOverrides=""></trim>:截取并拼接
prefix:在操作的SQL语句前加入某些内容
suffix:在操作的SQL语句后加入某些内容
prefixOverrides:把操作的SQL语句前的某些内容去掉
suffixOverrides:把操作的SQL语句后的某些内容去掉
5.3.1mapper.xml
当要去掉的内容有多种可能性时:用 | 分割
<!--去掉SQL语句后多余的 and 和 or 关键字-->
<trim prefix="where" suffixOverrides="and|or"></trim>
<select id="getEmpByMore" resultType="com.guolian.bean.Emp">
SELECT * FROM emp
<trim prefix="where" suffixOverrides="and">
<if test="eid != null">
eid=#{eid} AND
</if>
<if test="ename !=null and ename != ''">
ename=#{ename} AND
</if>
<if test="age != null">
age=#{age} AND
</if>
<if test="sex == 1 or sex == 0">
sex=#{sex}
</if>
</trim>
</select>
5.4choose(when、otherwise)
相当于 if…..else if…….else if……else。
每一个 when 代表一个 else if
otherwise 代表 else。
<!--
<choose>:选择某一个when或otherwise执行
<when test="">SQL</when>:通过test表达式判断拼接SQL语句
.
.
.
<otherwise>SQL</otherwise>:当when都不符合条件,就会选择otherwise拼接SQL语句。(可以不写)
</choose>
-->
5.4.1mapper.xml
<select id="getEmpListByChoose" resultType="com.guolian.bean.Emp">
SELECT eid,ename,age,sex FROM emp
WHERE
<choose>
<when test="eid != null">
eid = #{eid}
</when>
<when test="ename != null and ename != ''">
ename = #{ename}
</when>
<when test="age != null">
age = #{age}
</when>
<otherwise>
sex = #{sex}
</otherwise>
</choose>
</select>
<!--void insertEmp(Emp emp);-->
<insert id="insertEmp">
INSERT INTO emp VALUES(null,#{ename},#{age},
<choose>
<when test="sex == 0">'女'</when>
<when test="sex == 1">'男'</when>
</choose>
,NULL
)
</insert>
5.5foreach
foreach:对一个数组或集合进行遍历
collection:指定要遍历的集合或数组
item:设置别名
close:设置循环体的结束内容
open:设置循环体的开始内容
separator:设置每一次循环之间的分隔符
index:若遍历的list集合,代表下标
若遍历的Map集合,代表键
<foreach collection="" item="" close="" open="" separator="" index="">
</foreach>
5.5.1mapper.xml(实现批量删除)
之所以在 collection里写list,是因为:
当传输数据为集合或数组时
Mybatis会将其放入Map集合中。
List以list为键,Array以array为键。
如果忘记了键名,则可以用@Param(“”)自定义键名。
<!--void deleteMoreByList(List<Integer> eids);-->
<delete id="deleteMoreByList">
DELETE FROM emp WHERE eid IN
(
<foreach collection="list" item="eid" separator=",">
#{eid}
</foreach>
)
</delete>
两种写法效果一模一样,下面的改用 open和close 来设置()
<!--void deleteMoreByList(List<Integer> eids);-->
<delete id="deleteMoreByList">
DELETE FROM emp WHERE eid IN
<foreach collection="list" item="eid" open="(" close=")" separator=",">
#{eid}
</foreach>
</delete>
5.6批量操作
<!--
delete:
delete from emp where eid in ();
delete from emp where eid = 1 or eid = 2 or eid = 3;:
select:
select * from emp where eid in ();
select * from emp where eid = 1 or eid = 2 or eid = 3;:
update:
把每条数据修改为相同内容
update emp set ... where eid in ();
update emp set ... where eid = 1 or eid = 2 or eid = 3;:
把每条数据修改为相对应的内容
注意:必须在preperties文件里链接地址(URL)后添加参数:?allowMultiQueries=true
update emp set ... where eid = 1;
update emp set ... where eid = 2;
update emp set ... where eid = 3;
insert
insert into emp values(),(),(),();
-->
5.6.1批量添加
mapper.xml:
<!--void insertMoreByArray(@Param("emps") Emp[] emps);-->
<insert id="insertMoreByArray">
INSERT INTO emp VALUES
<foreach collection="emps" item="emps" separator=",">
(null,#{emps.ename},#{emps.age},#{emps.sex},1)
</foreach>
</insert>
test.java:
@Test
public void testTnsertMoreByArray() throws IOException {
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//Emp[] emps = new Emp[2];
//Emp[] emps = new Emp[]{};
Emp emp1 = new Emp(null,"a",23,"男");
Emp emp2 = new Emp(null,"ab",23,"男");
Emp emp3 = new Emp(null,"abc",23,"男");
Emp[] emps = {emp1,emp2,emp3};
mapper.insertMoreByArray(emps);
}
5.6.2批量修改
注意:预编译不允许一次性执行多条SQL语句。
所以,需要在properties文件里配置:?allowMultiQueries=true
允许一次性执行多条SQL语句。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?allowMultiQueries=true
jdbc.username=root
jdbc.password=3306
mapper.xml
<!--void updateMoreByArray(@Param("emps")Emp[] emps);-->
<update id="updateMoreByArray">
<foreach collection="emps" item="emp">
UPDATE emp SET ename = #{emp.ename},age = #{emp.age},sex = #{emp.sex} WHERE eid = #{emp.eid};
</foreach>
</update>
test.xml:
@Test
public void testUpdateMoreByArray() throws IOException {
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp1 = new Emp(20,"a1",233,"女");
Emp emp2 = new Emp(21,"ab1",233,"女");
Emp emp3 = new Emp(22,"abc1",233,"女");
Emp[] emps = {emp1,emp2,emp3};
mapper.updateMoreByArray(emps);
}
5.7sql标签
<!--
<sql id=""></sql>:设置一段SQL片段,即公共的SQL,可以被当前映射文件中所有的SQL语句所访问
<include refid=""></include>:访问某个SQL片段
-->
<sql id="selectEmpColumns">
SELECT eid,ename,age,sex FROM emp
</sql>
<!--List<Emp> getEmpListByChoose(Emp emp);-->
<select id="getEmpListByChoose" resultType="com.guolian.bean.Emp">
<include refid="selectEmpColumns"></include>
WHERE
<choose>
<when test="eid != null">
eid = #{eid}
</when>
<when test="ename != null and ename != ''">
ename = #{ename}
</when>
<when test="age != null">
age = #{age}
</when>
<otherwise>
sex = #{sex}
</otherwise>
</choose>
</select>
6.Mybatis的缓存
缓存里存放的是数据,而不是对象。
6.1一级缓存的使用
/**
* mybatis中的一级缓存默认开启,是SqlSession级别的。
* 即同一个SqlSession对于一个Sql语句,执行之后就会存储在缓存中,
* 下次执行相同的Sql,直接从缓存中取。
**/
public void testCache() throws IOException {
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEid = mapper.getEmpByEid("14");
Emp empByEid1 = mapper.getEmpByEid("14");
System.out.println(empByEid);
System.out.println("------------------------------");
System.out.println(empByEid1);
}
public void testCache() throws IOException {
//由于一级缓存是Sqlsession级别的,所以必须是不同的两个SqlSession才会使缓存失效
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEid = mapper.getEmpByEid("14");
System.out.println(empByEid);
System.out.println("------------------------------");
SqlSessionFactory sqlSessionFactort1 = getSqlSessionFactort();
SqlSession sqlSession1 = sqlSessionFactort1.openSession(true);
EmpMapper mapper1 = sqlSession1.getMapper(EmpMapper.class);
Emp empByEid1 = mapper1.getEmpByEid("14");
System.out.println(empByEid1);
}
6.2一级缓存失效的几种情况
1.不同的SqlSession对应不同的一级缓存
2.同一个SqlSession 但是查询条件不同
3.同一个SqlSession 两次查询期间执行了任何一次增删改操作
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEid = mapper.getEmpByEid("14");
System.out.println(empByEid);
System.out.println("------------------------------");
//执行了删除操作
mapper.deleteMoreEmp("1");
Emp empByEid1 = mapper.getEmpByEid("14");
System.out.println(empByEid1);
4.同一个SqlSession 两次查询期间手动清空了缓存。
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEid = mapper.getEmpByEid("14");
System.out.println(empByEid);
//清空缓存
sqlSession.clearCache();
System.out.println("------------------------------");
Emp empByEid1 = mapper.getEmpByEid("14");
System.out.println(empByEid1);
6.3二级缓存
1.二级缓存,全局作用域缓存,是SqlSessionFactory级别的。
2.二级缓存默认不开启,需要手动配置
3.Mybatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
4.二级缓存在SqlSession关闭或提交之后才会生效。
5.二级缓存使用的步骤:
(1).全局配置文件中开启二级缓存
<setting name="chcheEnabled" value="true"/>
(2).在需要使用二级缓存的映射文件处使用cache配置缓存
<!--
可以什么属性都不用写,只加 <cache/>就开启了二级缓存
eviction:缓存回收策略
LRU:最近最少使用,移除最长时间不被使用的
FIFO:先进先出,移除最先进入缓存的
SOFT:软引用
WEAK:弱引用
默认LRU
flushInterval:刷新间隔,单位是毫秒
默认是不设置
size:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly:只读,true/false
true:只读缓存,会给调用者返回存储对象的相同实例,这些对象不能被修改
false:读写缓存,会返回缓存对象的拷贝。
默认是false
type:设置第三方缓存,里面写Cache接口的实现类,一个实现类对应一种缓存方式
-->
<cache eviction="" flushInterval="" size="" readOnly="" type=""/>
(3).POJO需要实现Serializable接口
6.3.1测试
public void testSecondCache() throws IOException {
SqlSessionFactory sqlSessionFactort = getSqlSessionFactort();
SqlSession sqlSession = sqlSessionFactort.openSession(true);
EmpMapper mapper1 = sqlSession.getMapper(EmpMapper.class);
EmpMapper mapper2 = sqlSession.getMapper(EmpMapper.class);
EmpMapper mapper3 = sqlSession.getMapper(EmpMapper.class);
Emp empByEid1 = mapper1.getEmpByEid("14");
System.out.println(empByEid1);
//提交Sqlsession
sqlSession.commit();
System.out.println("---------------------------");
Emp empByEid2 = mapper2.getEmpByEid("14");
System.out.println(empByEid2);
//提交Sqlsession
sqlSession.commit();
System.out.println("---------------------------");
Emp empByEid3 = mapper3.getEmpByEid("14");
System.out.println(empByEid3);
}
6.4缓存的相关属性设置
1.全局setting里的cacheEnable 属性的配置 (配置二级缓存)
**配置二级缓存的开关,一级缓存一直是打开的**。
2.select标签的useCache属性
**配置这个select是否使用二级缓存,一级缓存是一直打开的**。
3.sql标签的flushCache属性 (刷新缓存)
增删改默认flushCache=true。执行以后,**会同时清空一级和二级缓存**。
查询默认flushCache=false。
4.sqlSession.clearCache(); 只是用来清空一级缓存。
6.5整合第三方缓存
MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。
6.5.1整合EhCache缓存的步骤
1.导入ehcache包,以及整合包、日志包
ehcache-core-2.6.8.jar(核心jar包)
mybatis-ehcache-1.0.3.jar(由Mybatis开发,来使用ehcache作为缓存功能的jar包)
slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar (管理日志的jar包)
2.在映射文件里导入第三方缓存。
type里写:缓存实现类的全类名。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.正常使用。与之前的二级缓存一样。