这学期开了Javaweb的课,本来想着顺便跟着学学,没想到一节公开课,一节实验课,课堂效果依旧那样还是自己学吧。
Releases · mybatis/mybatis-3 (github.com)
apache-maven:Maven – Download Apache Maven
MySQL版本:MySQL 5.7
MyBatis 版本: MyBatis 3.5.10
设置好maven选项
新建个maven项目,修改打包方式:jar ,pom.xml加上
即可
依赖
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
</dependencies>
创建个数据库
在resources中创建核心文件mybatis-config.xml
mysql5的JDBC驱动,使用com.mysql.jdbc.Driver
mysql8的JDBC驱动,使用com.mysql.cj.jdbc.Driver
mysql5的url:jdbc:mysql://localhost:3306/ssm
mysql8的url:jdbc:mysql://llocalhost:3306/ssm?serverTimezone=UTC
我用的是mysql5所以对应的driver和url也是5版本的,value、password就是数据库的用户名密码,这里的最下边的mappermybatis映射文件在后边会创建
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>
<!--引入mybatis映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
package com.sentiment.mapper;
public interface UserMapper {
int insertUser();
}
相关概念:ORM(Object Relationship Mapping)对象关系映射。
Java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
1、映射文件的命名规则: 表所对应的实体类的类名+Mapper.xml 例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml 因此一个映射文件对应一个实体类,对应一张表的操作 MyBatis映射文件用于编写SQL,访问以及操作表中的数据 MyBatis映射文件存放的位置是src/main/resources/mappers目录下
2、MyBatis中可以面向接口操作数据,要保证两个一致: a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致 b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
注意事项:
mapper接口和映射文件要保证两个一致:
1.mapper接口的全类名和映射文件的namespace一致
2.mapper接口中的方法的方法名要和映射文件中的sqL的id保持一致
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">
<mapper namespace="com.sentiment.mapper.UserMapper">
<insert id="insertUser">
insert into t_user values(null,'Sentiment','123456',20,'男','123456@qq.com')
</insert>
</mapper>
之后将mybatis-config.xml中的mapper映射文件加进去,即:
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
最后的目录结构:
package com.sentiment.test;
import com.sentiment.mapper.UserMapper;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisTest {
@Test
public void testInsert() throws IOException {
//获取Mybatis核心配置文件的输入流
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//获取sql的会话对象SqlSeesion,是MyBatis提供的操作数据库的对象,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
SqlSession sqlSession = sqlSessionFactory.openSession();
//如果加上参数true则操作的sql对象则会自动提交,无需commit方法
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//通过代理模式创建UserMapper接口的代理实现类对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//调用mapper接口中的方法,实现插入功能
int result = mapper.insertUser();
System.out.println(result);
//提交事务
sqlSession.commit();
sqlSession.close();
}
}
执行后则会根据UserMapper.xml中的文件执行插入语句
下边实现insertUser功能的语句还可以进行优化
优化前:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.insertUser();
优化后:
int result = sqlSession.insert("com.sentiment.mapper.UserMapper.insertUser");
依赖
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
配置好后运行,则会输出日志信息
日志的级别 FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左往右日志信息越来越详细
先写个工具类
package com.sentiment.utils;
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 java.io.IOException;
import java.io.InputStream;
public class SqlSeesionUtils {
public static SqlSession getSqlSession(){
SqlSession sqlSession;
try {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(is);
sqlSession = build.openSession(true);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sqlSession;
}
}
修改配置文件UserMapper.xml,加上update语法
<update id="updateUser">
update t_user set username='tana' where id=2;
</update>
接口类也实现一下对应的方法
public interface UserMapper {
int insertUser();
int updateUser();
}
之后定义个修改的方法testUpdate(),直接通过定义的工具类中获取SqlSeesion即可
public void testUpdate(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.updateUser();
System.out.println(result);
sqlSession.close();
}
和上边同理
UserMapper.xml
<delete id="deleteUser">
delete from t_user where id=3;
</delete>
添加接口
int deleteUser();
最终实现
public void testDelete(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.deleteUser();
System.out.println(result);
sqlSession.close();
}
接口
User getUserId();
List<User> getUserList();
UserMaaper.xml
<select id="getUserId" resultType="com.sentiment.pojo.User">
select * from t_user where id=1
</select>
<select id="getUserList" resultType="com.sentiment.pojo.User">
select * from t_user;
</select>
实现
@Test
public void testGetUserId(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserId();
System.out.println(user);
sqlSession.close();
}
@Test
public void testGetUserList(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
userList.forEach(System.out::println);
}
注意:
1、查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系 resultType:自动映射,用于属性名和表中字段名一致的情况 resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
2、当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异常 TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合作为返回值
configuration(配置)
注意:mybatis配置文件中标签的顺序是指定的依次是:
properties(属性)->settings(设置)->typeAliases(类型别名)->environments(环境配置)->mappers(映射器)
设置个jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/Mybatis
jdbc.username=root
jdbc.password=123456
导入到配置文件mybatis-config.xml中
<properties resource="jdbc.properties"/>
之后就可以将datasource部分进行替换
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<?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>
<!--引l入properties文件,此后就可以在当前文件中使用${ key}的方式访问value-->
<properties resource="jdbc.properties"/>
<!--
environments:配置链接数据库的环境
属性:default:设置默认使用环境的id
-->
<environments default="development">
<!--
environment:设置一个具体的链接数据库的环境
属性:id:设置数据库的唯一表示,不能重复
-->
<environment id="development">
<!--
transactionManager:设置事务管理器
属性:
type:设置事务管理的方式
type="JDBC /MANAGED”
JDBC:表示使用JDBC中原生的事务管理方式
MANAGED:被管理,例如Spring
-->
<transactionManager type="JDBC"/>
<!--
datasource:设置数据源
属性:
type:设置数据源的类型
type="PoOLED/ UNPOOLED | JNDI"
POOLED:表示使用数据库连接池
UNPOOLED:表示不使用数据库连接池了
JNDI :表示使用上下文中的数据源
-->
<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="test">
<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>
<!--引入mybatis映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
注:该标签根据顺序需放在properties和setting标签后
<typeAliases>
<package name="com.sentiment.pojo"/>
typeAliases>
若需要同时导入多个mapper.xml配置文件时,也可以包的形式全部导入,但需要满足如下两点:
<mappers>
<!-- <mapper resource="mappers/UserMapper.xml"/> -->
<!--
以包的方式引入映射文件,但是必须满足两个条件:
1、mapper接口和映射文件所在的包必须一致
2、mapper接口的名字和映射文件的名字必须一致
-->
<package name="com.sentiment.mapper"/>
</mappers>
File -> Setting -> Editor -> File and Code Templates
MyBatis获取参数值的两种方式:${}和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
KaTeX parse error: Expected 'EOF', got '#' at position 51: …赋值时,需要手动加单引号;但是#̲{}使用占位符赋值的方式拼接s…{}可以有效的避免sql注入)
若mapper接口中的方法参数为单个的字面量类型
此时可以使用KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以任意的名称获取参数的值,…{}需要手动加单引号
接口
User getUsername(String username);
mapper配置文件
<select id="getUsername" resultType="User">
<!-- select * from t_user where username=#{username} -->
select * from t_user where username='${username}'
</select>
实现
public void GetUsername(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=mapper.getUsername("Sentiment");
System.out.println(user);
}
若mapper接口方法的参数为多个的字面量类型
此时Mybatis会将参数放在map集合中,以两种方式存储(也可arg,param混合使用):
接口
User checklogin(String username ,String password);
配置文件
<select id="checklogin" resultType="User">
select * from t_user where username='${param1}' and password='${param2}'
select>
若mapper接口中的方法需要多个参数时,此时可以手动创建map集合,讲这些数据放在map中,并通过${}或#{}访问map集合的键就可以获取对应的值
接口
User checkLoginByMap(Map<String,Object> map);
配置文件
<select id="checkLoginByMap" resultType="User">
select * from t_user where username='${username}' and password='${password}'
select>
实现
public void CheckLoginByMap(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<>();
map.put("username","Sentiment");
map.put("password",123456);
User user=mapper.checkLoginByMap(map);
System.out.println(user);
}
需要通过#{}和${}访问实体类中的属性就可以获取相对应的属性值
接口
int insertUser(User user);
配置文件
<insert id="insertUser">
insert into t_user values(#{id},#{username},#{password},#{age},#{gender},#{email})
insert>
实现
public void insertUser(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=new User(4,"xxx","123456",20,"男","[email protected]");
mapper.insertUser(user);
}
通过接口定义@Param,此时mybatis会将注解中的参数放在map中进行存储,有两种存储方式:
接口
User checkLoginByParam(@Param("username") String username,@Param("password") String password);
配置文件
<select id="checkLoginByParam" resultType="User">
select * from t_user where username=#{username} and password=#{password}
select>
实现
public void checkLoginByParam(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=mapper.checkLoginByParam("Sentiment","123456");
System.out.println(user);
}
}
接口
Map<String,Object> getUserByIdToMap(@Param("id") int id);
配置文件
<select id="getUserByIdToMap" resultType="map">
select * from t_user where id=#{id}
select>
实现
public void getUserByIdToMap(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
Map<String, Object> map = mapper.getUserByIdToMap(2);
System.out.println(map);
}
由于是多个map并不能放到一个map中,所以这里用的是list
接口
List<Map<String ,Object>> getAllUser();
但除此外,Map可以通过@MapKey来存储键值,从而完成map中存储多条数据
@MapKey("id")
Map<String ,Object> getAllUser();
用like关键字实现模糊查询
接口
List<User> getAllUserBySpecial(@Param("mohu") String mohu);
配置文件
有以下三种查询方式:
<select id="getAllUserBySpecial" resultType="User">
select * from t_user where username like concat('%',#{mohu},'%')
select>
实现
这里以模糊查询username带字母"n"的为例
@Test
public void getAllUserBySpecial(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
List<User> name = mapper.getAllUserBySpecial("n");
name.forEach(System.out::println);
}
接口
int deleteMore(@Param("ids") String ids);
#也可以用between and 的方式,但这种方式需要两个参数start,end
int deleteMore(@Param("ids") String ids,@Param("ide")String ide);
配置文件
delete from t_user where id in (${ids})
#between
delete from t_user where id between ${ids} and ${ide}
实现
public void deleteMore(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
mapper.deleteMore("5,6");
#mapper.deleteMore("5","6"); #between方式
}
接口
List<User> changeTableName(@Param("tablename") String tablename);
配置文件
<select id="changeTableName" resultType="User">
select * from ${tablename}
select>
实现
public void changeTableName(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
List<User> list = mapper.changeTableName("t_user");
list.forEach(System.out::println);
}
这种方式主要建立于主键自增的基础上,可以通过mybatis语句,获取自增后的属性值
接口
void insertUser(User user);
配置文件
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
insert>
实现
public void insertUser(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
SpecialSqlMapper mapper = sqlSession.getMapper(SpecialSqlMapper.class);
User user = new User(null, "xxx", "123456", 20, "男", "[email protected]");
mapper.insertUser(user);
System.out.println(user);
}
未设置时:
设置后:
先建两张表
并创建对应的pojo和mapper
字段设置的是emp_id,emp_name,而在java中应用的是驼峰命名法,所以定义的属性名是empId,empName,这就导致了一个属性名不同而无法完成映射进行查询的问题
接口
Emp getUserByEmpId(@Param("id") int id);
配置文件
主要有三种方式
<select id="getUserByEmpId" resultType="Emp">
<!-- select emp_id empId ,emp_name empName ,age ,gender from t_emp where emp_id=${id} -->
select * from t_emp where empid=${id}
</select>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
实现
public void getUserByEmpId(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp result = mapper.getUserByEmpId(1);
System.out.println(result);
}
<resultMap id="empresultMap" type="emp">
<id column="emp_id" property="empId">id>
<result column="emp_name" property="empName">result>
resultMap>
<select id="getUserByEmpId" resultMap="empresultMap">
select * from t_emp where emp_id=${id}
select>
resultMap:设置自定义映射关系
id:唯一标识
type:处理映射关系的实体类类型
常用标签
通过员工id查询对应部门名称等信息
接口
Emp getEmpAndDeptByEmpId(@Param("id") int id);
配置文件
<select id="getEmpAndDeptByEmpId" resultType="Emp">
select * from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where emp_id=${id}
select>
实现
public void getEmpAndDeptByEmpId(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp result = mapper.getEmpAndDeptByEmpId(1);
System.out.println(result);
}
查询结果
Emp{empId=1, empName='Sentiment', age=20, gender='男', dept=null}
dept值为null,采用左链接的方式进行查询,但在Emp类中定义的是private Dept dept;,无法将属性名dept对应到数据库中的字段dept_id、dept_name,且需要注意的另一个问题是 resultType返回的是Emp类型的,而查询的t_dept中的数据是Dept的实现类型,明显不可以,所以有了下边三种解决办法
同解决属性和字段名不一致的情况,设置一个resultMap自定义映射
由于属性dept属性是Dept类型的,所以dept.deptId就可以代表Dept类中的deptId属性
<resultMap id="EmpAndDeptByEmpId" type="Emp">
<id column="emp_id" property="empId">id>
<result column="emp_name" property="empName">result>
<result column="dept_id" property="dept.deptId">result>
<result column="dept_name" property="dept.deptName">result>
resultMap>
<select id="getEmpAndDeptByEmpId" resultMap="EmpAndDeptByEmpId">
select * from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where emp_id=${id}
select>
此时的运行结果
Emp{empId=1, empName='Sentiment', age=20, gender='男', dept=Dept{deptId=1, deptName='A'}}
association :处理多对一的映射关系(处理实体类类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性的类型
<resultMap id="EmpAndDeptByEmpId" type="Emp">
<id column="emp_id" property="empId">id>
<result column="emp_name" property="empName">result>
<association property="dept" javaType="Dept">
<id column="dept_id" property="deptId">id>
<result column="dept_name" property="deptName">result>
association>
resultMap>
property:设置需要处理映射关系的属性的属性名
select:可以理解为property中属性的值是从select中的类获取来的
column:将查询出的某个字段作为分布查询的sql条件
接口
Emp getEmpAndDeptByStepOne(@Param("id") int id);
配置文件
<resultMap id="EmpAndDeptByEmpId" type="Emp">
<id column="emp_id" property="empId">id>
<result column="emp_name" property="empName">result>
<association property="dept"
select="com.sentiment.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="dept_id">
association>
resultMap>
<select id="getEmpAndDeptByStepOne" resultMap="EmpAndDeptByEmpId">
select * from t_emp where emp_id = #{id}
select>
新建个DeptMapper接口
Dept getEmpAndDeptByStepTwo(@Param("deptId") int deptId);
并设置对应的DeptMapper.xml配置文件
<mapper namespace="com.sentiment.mapper.DeptMapper">
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where dept_id=#{deptId}
select>
mapper>
实现
public void getEmpAndDeptStep(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp result = mapper.getEmpAndDeptByStepOne(1);
System.out.println(result);
}
捋一下思路:
先调用getEmpAndDeptByStepOne(1)方法,之后会执行配置文件中定义的语句—select * from t_dept where dept_id=#{deptId}
由于设置的是resultMap自定义映射,因此会将上边column中自定义执行语句中的dept_id结果,当做条件去执行
getEmpAndDeptByStepTwo()方法,也就执行了select * from t_dept where dept_id=#{deptId},而deptId由于设置了settings也
就相当于获取dept_id的值,而这个值就是column中设置的通过getEmpAndDeptByStepOne()获取的dept_id
分布查询的一个优点就是延迟加载
假设前边的输出结果换成只输出员工名
System.out.println(result.getEmpName());
此时执行后,可以发现若只需获取员工名,其实只从t_emp进行查询就可以了,但是分布查询仍然会执行查询t_dept的语句,影响了查询速度等。
此时就可以用延时加载来节约资源,但在使用前需要进行全局配置
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
设置后mybatis就会按需查询,不再调用t_dept表
前边两个是全局配置,而如果设置多个分布查询后,若某个查询无需分布查询,则可在对应的mapper中设置fetchType属性即可
<association property="dept" fetchType="eager"
select="com.sentiment.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="dept_id">
association>
这里设置的是eager所以,本次查询将不再适用延时加载,在获取员工姓名时仍会查询t_dept表
collection:处理一对多的映射关系(处理集合类型的属性)与association相对应
对多用集合private List
,在Dept类中添加个属性,并设置对应的setter,getter,toString等
接口
Dept getDeptAndEmpByDeptId(@Param("deptId") int deptId);
DeptMapper.xml
property:设置需要处理映射关系的集合名
ofType:设置集合类型的属性中存储的数据类型 与javaType相对应只是一个设置属性类型一个是集合类型
<resultMap id="deptAndEmpResultMap" type="Dept">
<id column="dept_id" property="deptId">id>
<result column="dept_name" property="deptName">result>
<collection property="emps" ofType="Emp">
<id column="emp_id" property="empId">id>
<result column="emp_name" property="empName">result>
<result column="age" property="age">result>
<result column="gender" property="gender">result>
collection>
resultMap>
<select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.dept_id=t_emp.dept_id where t_dept.dept_id=#{deptId}
select>
实现
public void getDeptAndEmpByDeptId(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept result = mapper.getDeptAndEmpByDeptId(1);
System.out.println(result);
}
为了更好的理解这里将两步分开一步步来
先定义Dept接口
Dept getDeptAndEmpByStepOne(@Param("deptId") int deptId);
之后本次查询是一对多的查询,所以现将t_dept表查出来,resultMap等等以后再填
<select id="getDeptAndEmpByStepOne" resultMap="">
select * from t_dept where dept_id =#{deptId}
select>
接着定义resultMap,要获取的是员工集合,所以property填之前定义好的集合emps,而select的意思是emps集合的类型是从哪获取的,所以先空出来去定义对应的集合类型
<resultMap id="deptAndEmpByStepOne" type="Dept">
<id column="dept_id" property="deptId">id>
<result column="dept_name" property="deptName">result>
<collection property="emps"
select=""
column="">
collection>
resultMap>
定义Emp集合接口
List<Emp> getDeptAndEmpByStepTwo(@Param("id") int id);
通过配置文件实现
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where emp_id = #{id}
select>
现在就有了select即getDeptAndEmpByStepTwo的路径,而两个库是通过dept_id链接的,所以填写到column中,最后再将select标签中的resultMap填上
<resultMap id="deptAndEmpByStepOne" type="Dept">
<id column="dept_id" property="deptId">id>
<result column="dept_name" property="deptName">result>
<collection property="emps"
select="com.sentiment.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id">
collection>
resultMap>
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByStepOne">
select * from t_dept where dept_id =#{deptId}
select>
实现
public void getDeptAndEmpByStep(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept result = mapper.getDeptAndEmpByStepOne(1);
System.out.println(result);
//由于前边设置了延迟加载所以,也可以通过下边方式进行测试
System.out.println(result.getDeptName());
}
由于一对多是集合的方式,不像多对一时通过Dept类型的dept属性进行映射(
),所以级联便不再适用
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行
接口
List<Emp> getEmpByCondition(Emp emp);
配置文件
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp where
<if test="empName !=null and empName !=''">
emp_name=#{empName}
if>
<if test="age !=null and age !=''">
and age=#{age}
if>
<if test="gender !=null and gender !=''">
and gender=#{gender}
if>
select>
实现
public void getEmpByCondition(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class);
Emp emp = new Emp(1, "Sentiment", 20, "男");
List<Emp> result = mapper.getEmpByCondition(emp);
result.forEach(System.out::println);
}
在使用if标签时存在一个问题:若第一个条件不成立,第二个条件成立的话 ,则第一个标签不会执行,但由于第二个标签中存在and关键字若第一个标签不执行直接执行第二个标签的话,会将and直接拼接到where 后边,导致语句执行失败即:
此时就有了三个解决办法:
在where后边加上恒成立条件1=1,之后的每个if标签的语句前都加上and,此时无论标签执不执行,语句都成立
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp where 1=1
<if test="empName !=null and empName !=''">
and emp_name=#{empName}
if>
<if test="age !=null and age !=''">
and age=#{age}
if>
<if test="gender !=null and gender !=''">
and gender=#{gender}
if>
select>
在if标签前加上where标签:
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<where>
<if test="empName !=null and empName !=''">
emp_name=#{empName}
if>
<if test="age !=null and age !=''">
and age=#{age}
if>
<if test="gender !=null and gender !=''">
and gender=#{gender}
if>
where>
select>
除了前两种方法外,还可以使用trim标签
prefix/suffix:在内容前/后加上指定内容
prefixOverrides/suffixOverrides:在内容前/后去掉多余的指定内容
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<trim prefix="where" prefixOverrides="and">
<if test="empName !=null and empName !=''">
emp_name=#{empName}
if>
<if test="age !=null and age !=''">
and age=#{age}
if>
<if test="gender !=null and gender !=''">
and gender=#{gender}
if>
trim>
select>
when相当于ifelse、otherwse相当于else,所以when至少有一个而otherwise至多有一个,都放在choose标签中
when语句中不需要加and,因为when相当于ifelse语句,所以三个when标签至多只会执行一个
<select id="getEmpByChoose" resultType="Emp">
select * from t_emp
<where>
<choose>
<when test="empName !=null and empName !=''">
emp_name=#{empName}
when>
<when test="age !=null and age !=''">
age=#{age}
when>
<when test="gender !=null and gender !=''">
gender=#{gender}
when>
choose>
where>
select>
collection:设置循环中的数组或集合
item:表示数组或集合中的每一个数组
separator:每次循环之间数据的分隔符
index:指定一个名字,用于表示在循环过程中,每次循环到的位置
open:表示该循环语句以什么开始
close:表示以什么结束
接口
void insertMoreEmp(@Param("emps") List<Emp> emps);
配置文件
由于emps是集合类型,所以需要用item=emp,设置每次执行的迭代名,separator=","以逗号分割每次循环
<insert id="insertMoreEmp">
insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(#{emp.empId},#{emp.empName},#{emp.age},#{emp.gender},null)
foreach>
insert>
实现
public void insertMoreEmp(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class);
Emp emp1 = new Emp(5, "aaa", 20, "男");
Emp emp2 = new Emp(6, "bbb", 20, "男");
Emp emp3 = new Emp(7, "ccc", 20, "男");
List<Emp> emps = Arrays.asList(emp1, emp2, emp3);
mapper.insertMoreEmp(emps);
}
接口
void deleteMoreEmp(@Param("empids") int[] empids);
配置文件
open、close设置语句开头结尾,分隔符也可以用or
<delete id="deleteMoreEmp">
delete from t_emp where emp_id in
<foreach collection="empids" item="empid" separator="," open="(" close=")">
#{empid}
</foreach>
</delete>
实现
public void deleteMoreEmp(){
SqlSession sqlSession = SqlSeesionUtils.getSqlSession();
DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class);
int[] ints = {5, 6, 7};
mapper.deleteMoreEmp(ints);
}
可以记录一段sql,在需要用的她方使用include标签进行引用
<sql id="sqls">
emp_id,emp_name,age,gender
sql>
<select id="getEmpByCondition" resultType="Emp">
select <include refid="sqls">include> from t_emp
select>
MyBatis的一级缓存是sqlSession级别的,即通过同一个sqLSession查询的数据会被缓存,再次使用同一个sqlsession查询同一条数据,会从缓存中获到
emp1、emp2是从同一个sqlSeeion获取的,因此在查询同一条语句时,查询语句只执行了一次,第二次则会从缓存中查找
1、若在创建sqlSeesion2,区别于前一个sqlSession时,即使查询同一条语句 也会重新执行
2、前边的查询的是id=1,若后边改成id=2,缓存就会失效
Emp emp1=mapper1.getEmpById(1);
System.out.println(emp1);
Emp emp2 = mapper1.getEmpById(2);
System.out.println(emp2);
3、当执行插入或删除语句后,会自动清理缓存
Emp emp1=mapper1.getEmpById(1);
System.out.println(emp1);
mapper1.insertEmp(new Emp(5,"aaa",20,"男"));
Emp emp2 = mapper1.getEmpById(1);
System.out.println(emp2);
4、手动清理缓存后也会重新执行
Emp emp1=mapper1.getEmpById(1);
System.out.println(emp1);
sqlSession1.clearCache();
//mapper1.insertEmp(new Emp(5,"aaa",20,"男"));
Emp emp2 = mapper1.getEmpById(1);
System.out.println(emp2);
MyBatis的二级缓存是sqLsessionFactory级别的,即通过同一个sqLsessionFactory所获取的sqLsession查询的数据会被缓存,在通过同一个sqLSessionFactory所获取的sqlSession查询相同的数据会从缓存中获取
MyBatis二级缓存开启的条件:
二级缓存由于是sqlSessionFactory级别的,所以不再适用工具类,在满足上述条件后成功从缓存读取数据并且在二级缓存中会返回命中率等信息,但需要注意的是(存在增删改的操作时仍会清除缓存)
在mapper配置文件中添加的cache标签可以设置一些属性:
eviction属性:缓存回收策略,默认的是 LRU。
flushInterva属性:刷新间隔,单位毫秒
size属性:引用数目,正整数
readOnly属性:只读,true/false
依赖
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.1version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
ehcache.xml配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
<diskStore path="D:/Java_Web/Mybatis_cache/cache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
二级缓存类型
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
logback.xml
<configuration>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg %npattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
root>
<logger name="com.sentiment.mapper" level="DEBUG"/>
configuration>
此时执行,则会返回logback日志信息,并在指定目录生成缓存文件