文章目录
Mybatis
一、Mybatis介绍
Mybatis是一个半自动的ORM框架
ORM(Object Relational Mapping)对象关系映射,将java中的一个对象与数据表中一行记录一一对应
ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,实现对象的持久化。
Mybatis的前身是iBatis。
Mybatis中文网:MyBatis中文网
Mybatis的特点:
- 支持自定义SQL、存储过程
- 对原有的JDBC进行了封装,几乎消除了所有JDBC代码,让开发者只需关注SQL本身
- 支持XML和注解配置方式自定义完成ORM操作,实现结果映射
二、Mybatis基本操作
1、添加Mybatis依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
2、创建Mybatis配置文件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下可以配置多个不同id的environment,不同环境用不同的设置development,test..-->
<!--environments的default属性来指定使用哪个environment标签-->
<environments default="development">
<environment id="development">
<!--transactionManager标签用于配置数据库管理方式-->
<!--type="JDBC":可以进行事务的提交和回滚操作-->
<!--type="MANAGED":依赖容器完成事务管理,本身不进行事务的提交和回滚操作-->
<!-- <transactionManager type="MANAGED"/>-->
<transactionManager type="JDBC"/>
<!--POOLED|UNPOOLED-->
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=false"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--配置映射文件的位置-->
<mappers>
<mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>
</configuration>
3、创建表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(10) NOT NULL,
`name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(10) NOT NULL,
`gander` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `student` VALUES (1, '白杰', 19, '男');
INSERT INTO `student` VALUES (2, '连宇栋', 19, '男');
INSERT INTO `student` VALUES (3, '邸志伟', 24, '男');
INSERT INTO `student` VALUES (4, '杰杰高', 11, '男');
INSERT INTO `student` VALUES (5, '中杰男', 18, '男');
INSERT INTO `student` VALUES (6, '武三水', 18, '女');
INSERT INTO `student` VALUES (7, '张志伟', 16, '男');
INSERT INTO `student` VALUES (8, '康永亮', 23, '男');
INSERT INTO `student` VALUES (9, '杨涛瑞', 22, '女');
INSERT INTO `student` VALUES (10, '王杰', 21, '男');
INSERT INTO `student` VALUES (11, 'fansl', 15, '男');
4、创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int id;
private String name;
private int age;
private String gander;
}
5、创建DAO定义接口方法
public interface StudentDAO {
int insertStudent(Student student);
}
6、创建DAO接口的映射文件
<?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指定Dao接口全限定名-->
<mapper namespace="com.ebay.dao.StudentDAO">
<!--id要和接口方法名字保持一致;parameterType是指方法需要的参数类型-->
<insert id="insertStudent" parameterType="com.ebay.pojo.Student">
insert into student values(#{id},#{name},#{age},#{gander})
</insert>
</mapper>
7、测试添加
@Test
public void insertStudent() {
try {
//加载Mybatis配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//新建一个factory,他的作用用来生产sqlSeesion
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
System.out.println(studentDAO);
int i = studentDAO.insertStudent(new Student(21,"路飞",19,"男"));
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
}
}
8、MybatisUtil工具类
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
static {
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//新建一个factory,他的作用用来生产sqlSeesion
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(boolean isAutoCommit) {
SqlSession sqlSession = local.get();
if (sqlSession == null) {
//通过SqlSessionFactory调用openSession方法获取对象时,可以通过参数设置事务是否自动提交
sqlSession = factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object> T getMapper(Class<T> c) {
SqlSession sqlSession = getSqlSession(true);
return sqlSession.getMapper(c);
}
9、事务管理
自动提交事务
通过SqlSessionFactory调用openSession方法获取对象时,可以通过参数设置事务是否自动提交
如果参数设置true,表示自动提交事务factory.openSession(true)
手动提交事务
当有多个事务操作时,自动提交会出现数据一致性问题,用手动提交
通过SqlSessionFactory调用openSession方法获取对象时,可以通过参数设置事务是否自动提交
如果参数设置false,表示手动提交事务factory.openSession(false)
SqlSession sqlSession=MybatisUtil.getSqlSession(false);
try {
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
System.out.println(studentDAO);
//事务操作1
int i = studentDAO.insertStudent(new Student(216,"小白",19,"男"));
//事务操作2...
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
e.printStackTrace();
}
三、Mybatis的CRUD操作
1、简单删改查操作
StudentDAO
//删除
int deleteStudentById(int id);
//修改
int updateStudentByName(Student student);
//查询所有
List<Student> selectAllStudent();
StudentMapper.xml
<delete id="deleteStudentById">
delete from student where id=#{id}
</delete>
<update id="updateStudentByName" parameterType="com.ebay.pojo.Student">
update student set`name`=#{name} ,age=#{age},gander=#{gander} where `id`=#{id}
</update>
<!--resultType:指定查询结果封装的对象的实体类-->
<!--resultMap标签用于定义实体类与数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.ebay.pojo.Student">
<!--column代表数据库列的名字,property对应实体类的属性值 -->
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="age" property="age"></result>
<result column="gander" property="gander"></result>
</resultMap>
<select id="selectAllStudent" resultMap="studentMap">
select * from student
</select>
StudentDAOTest.java测试类
@Test
public void deleteStudent() {
StudentDAO studentDAO= MybatisUtil.getMapper(StudentDAO.class);
studentDAO.deleteStudentById(21);
}
@Test
public void updateStudentByName() {
StudentDAO studentDAO = MybatisUtil.getMapper(StudentDAO.class);
studentDAO.updateStudentByName(new Student(216,"大白00",11,"女"));
}
@Test
public void selectAllStudent() {
StudentDAO studentDAO = MybatisUtil.getMapper(StudentDAO.class);
List<Student> studentList = studentDAO.selectAllStudent();
for (Student students: studentList) {
System.out.println(students.toString());
}
}
2、多参数查询
分页查询(参数 start,pageSize)
-
如果操作方法有一个Map类型的参数,在Mapper配置中可以直接通过#{key}获取key对应的value
-
在StudentDAO定义方法,如果方法有多个参数,用@Param
//分页查询
List<Student> selectStudentByPage(@Param("start")int start,@Param("pageSize")int pageSize);
StudentMapper.xml
<select id="selectStudentByPage" resultMap="studentMap">
select * from student limit #{start},#{pageSize}
</select>
StudentDAOTest.java测试类
@Test
public void selectStudentByPage() {
StudentDAO studentDAO = MybatisUtil.getMapper(StudentDAO.class);
List<Student> studentList = studentDAO.selectStudentByPage(1,4);
for (Student students: studentList) {
System.out.println(students.toString());
}
}
3、添加操作回填生成的主键
StudentMapper.xml
<!--useGeneratedKeys:设置添加操作是否需要回填生成的主键-->
<!--keyProperty:设置回填的主键值赋值到参数对象的哪个属性-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="id">
insert into student values(#{id},#{name},#{age},#{gander})
</insert>
四、Mybatis主配置文件
mybatis-config.xml是Mybatis框架的主配置文件,用于配置Mybatis数据源和属性信息
1、properties标签
用于设置键值对,或者加载属性文件
- 在resources目录下创建jdbc.properties
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/school?useSSL=false
mysql_username=root
mysql_password=root
在mybatis-config.xml通过properties标签引用jdbc.properties
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="${mysql_driver}"/>
<!--连接数据库的url字符串-->
<property name="url" value="${mysql_url}"/>
<!--访问数据库的用户名-->
<property name="username" value="${mysql_username}"/>
<!--密码-->
<property name="password" value="${mysql_password}"/>
</dataSource>
</environment>
2、settings标签
<settings>
<!--启动二级缓存-->
<setting name="cacheEnabled" value="true"/>
<!--启动延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
3、typeAliases标签
<!--typeAliases标签用于实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名-->
<typeAliases>
<typeAlias type="com.ebay.pojo.Student" alias="Student"></typeAlias>
</typeAliases>
4、plugins标签
<!--plugins标签,用于配置Mybatis插件(分页插件)-->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
五、映射文件
1、mapper根标签
mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名
2、insert标签
声明添加操作
常用属性
id属性,绑定对应DAO接口中的方法
parameterType属性,指定接口中对应方法的参数类型(可省)
useGeneratedKeys属性:设置添加操作是否需要回填生成的主键
keyProperty属性:设置回填的主键值赋值到参数对象的哪个属性
timeout属性:设置此操作的超时时间,如果不设置则一直等待
主键回填操作
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="id">
insert into student values(#{id},#{name},#{age},#{gander})
</insert>
<!--此处主键应设自增,上面的忘了 (-_-) -->
<insert id="insertStudent" >
<selectKey keyProperty="id" resultType="java.lang.Integer" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into student(`name`,age,gander) values(#{name},#{age},#{gander})
</insert>
3、delete标签
声明删除操作
4、update标签
声明修改操作
5、select标签
声明查询操作
id属性,指定绑定方法的方法名
parameterType属性,设置参数类型
resultType属性,指定当前sql返回数据封装的对象类型(实体类)
resultMap属性,指定从数据表到实体类的字段和属性的对应关系
useCache属性,指定此查询操作是否需要缓存
timeout属性,设置超时时间
6、resulltMap
<resultMap id="studentMap" type="com.ebay.pojo.Student">
<!--column代表数据库列的名字,property对应实体类的属性值 -->
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="age" property="age"></result>
<result column="gander" property="gander"></result>
</resultMap>
7、cache标签
设置当前DAO进行数据库操作时的缓存属性设置
<cache type="" size="" readOnly=""></cache>
8、sql和include
<sql id="stu"> id,`name`,age,gander </sql>
<select id="selectAllStudent" resultMap="studentMap">
select <include refid="stu"/> from student
</select>
六、分页插件PageHelper
分页插件是一个独立于Mybatis之外的第三方插件
1、添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
2、配置插件
在mybatis的主配置mybatis-config.xml通过plugins标签进行配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"> </plugin>
</plugins>
3、分页实例
StudentDAO studentDAO = MybatisUtil.getMapper(StudentDAO.class);
PageHelper.startPage(2,5);
List<Student> studentList = studentDAO.selectAllStudent();
PageInfo<Student> pageInfo=new PageInfo<Student>(studentList);
//pageInfo中包含了数据及分页信息
List<Student> list = pageInfo.getList();
for (Student stu: list) {
System.out.println(stu);
}
七、关联映射
1、一对一
用户和详情
1.1创建数据表
--用户信息表
CREATE TABLE users(
user_id int PRIMARY KEY AUTO_INCREMENT,
user_name VARCHAR(20) not null unique,
user_pwd VARCHAR(20) not null,
user_realname VARCHAR(20) not null,
user_img VARCHAR(100)not null
)
--用户详情表
CREATE TABLE details(
detail_id int PRIMARY KEY AUTO_INCREMENT,
user_adrr VARCHAR(20) not null,
user_tel VARCHAR(20) not null,
user_desc VARCHAR(200) not null
)
1.2创建实体类
User.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int userId;
private String userName;
private String userPwd;
private String userRealname;
private String userImg;
private Detail detail;
}
Detail.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Detail {
private int detailId;
private String userAddr;
private String userTel;
private String userDesc;
private int userId;
}
1.5添加操作(事务)
分两次
SqlSession sqlSession = MybatisUtil.getSqlSession(false);
try {
//user添加
Detail detail = new Detail(0, "江阴", "12580", "1111", 0);
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
User user = new User(0, "lisi", "11111", "李四", "01.img",detail);
userDAO.insertUser(user);
//detail添加
detail.setUserId(user.getUserId());
DetailDAO detailDAO = sqlSession.getMapper(DetailDAO.class);
detailDAO.insertDetail(detail);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
e.printStackTrace();
}
1.4一对一关联查询
映射文件
- 连接查询
UserMapper.xml
<resultMap id="userMap" type="com.ebay.pojo.User">
<id column="user_id" property="userId"/>
<result column="user_name" property="userName"/>
<result column="user_pwd" property="userPwd"/>
<result column="user_realname" property="userRealname"/>
<result column="user_img" property="userImg"/>
<result column="detail_id" property="detail.detailId"/>
<result column="user_addr" property="detail.userAddr"/>
<result column="user_tel" property="detail.userTel"/>
<result column="user_desc" property="detail.userDesc"/>
</resultMap>
<select id="queryUser" resultMap="userMap">
SELECT user_id,user_name,user_pwd,user_realname,user_img,detail_id,user_addr,user_tel,user_desc FROM users u INNER JOIN details d on u.user_id=d.uid WHERE u.user_name =#{userName}
</select>
- 子查询
DetailMapper.xml
<resultMap id="detailMap" type="com.ebay.pojo.Detail">
<id column="detail_id" property="detailId"/>
<result column="user_addr" property="userAddr"/>
<result column="user_tel" property="userTel"/>
<result column="user_desc" property="userDesc"/>
<result column="uid" property="userId"/>
</resultMap>
<select id="queryDetail" resultMap="detailMap">
select detail_id,user_addr,user_tel,user_desc from details where uid=#{uid}
</select>
UserMapper.xml
<resultMap id="userMap" type="com.ebay.pojo.User">
<id column="user_id" property="userId"/>
<result column="user_name" property="userName"/>
<result column="user_pwd" property="userPwd"/>
<result column="user_realname" property="userRealname"/>
<result column="user_img" property="userImg"/>
<!--association调用子查询,关联查询一个对象-->
<association property="detail" select="com.ebay.dao.DetailDAO.queryDetail" column="user_id"></association>
</resultMap>
<select id="queryUser" resultMap="userMap">
SELECT user_id,user_name,user_pwd,user_realname,user_img
FROM users
WHERE user_name =#{userName}
</select>
2、一对多
2.1创建数据库
CREATE TABLE classes(
cid int PRIMARY KEY auto_increment,
cname varchar(30) not null unique,
cdesc varchar(100)
);
CREATE TABLE students(
sid char(5) PRIMARY KEY,
sname VARCHAR(20)not null,
sage int not null,
scid int not null
)
2.2 创建实体类
Clazz.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Clazz {
private int classId;
private String className;
private String classDesc;
private List<Students> stus;
}
Students.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Students {
private int stuId;//学号
private String stuName;
private int stuAge;
private String stuCid;//学生所在班级的id
}
2.3一对多关联查询
- 连接查询
ClassMapper.xml
<resultMap id="classMap" type="com.ebay.pojo.Clazz">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="classDesc"/>
<!-- Clazz对象的stus属性是一个list集合,需要使用collection标签 -->
<!-- collection 的ofType属性声明集合中元素的类型 -->
<collection property="stus" ofType="com.ebay.pojo.Students">
<id column="sid" property="stuId"/>
<result column="sname" property="stuName"/>
<result column="sage" property="stuAge"/>
<result column="scid" property="stuCid"/>
</collection>
</resultMap>
<select id="queryClass" resultMap="classMap">
SELECT * FROM classes c INNER JOIN students s
on c.cid=s.scid
WHERE c.cid=#{classId}
</select>
- 子查询
ClassMapper.xml
<resultMap id="classMap" type="com.ebay.pojo.Clazz">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="classDesc"/>
<collection property="stus" select="com.ebay.dao.StudentsDAO.queryStudentsById" column="cid"/>
</resultMap>
<select id="queryClass" resultMap="classMap">
SELECT * FROM classes where cid=#{classId}
</select>
StudentsMapper.xml
<resultMap id="studentsMap" type="com.ebay.pojo.Students">
<id column="sid" property="stuId"/>
<result column="sname" property="stuName"/>
<result column="sage" property="stuAge"/>
<result column="scid" property="stuCid"/>
</resultMap>
<select id="queryStudentsById" resultMap="studentsMap">
select * from students where scid=#{stuCid}
</select>
多对一关联查询类似于一对一
3、多对多
3.1创建数据库
学生(m)对课程(n)
-- 学生信息表(如上)
-- 课程信息表
CREATE TABLE courses(
course_id int PRIMARY KEY auto_increment,
course_name varchar(50) not null
)
-- 选课信息表/成绩表(学号、课程号、成绩)
CREATE TABLE grades(
sid char(5) not null,
cid int not null,
score int not null
)
3.2关联查询
查询学生时,同时查询学生选择的课程
Student.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Students {
private int stuId;//学号
private String stuName;
private int stuAge;
private List<Course> courses;//学生选择的课程
}
Course.java
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Course {
private int courseId;
private String courseName;
}
根据课程编号查询课程时,同时查询选择了这门课程的学生
Student.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Students {
private int stuId;//学号
private String stuName;
private int stuAge;
}
Course.java
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Course {
private int courseId;
private String courseName;
private List<Student> students;
}
CourseMapper.xml
<resultMap id="courseMap" type="com.ebay.pojo.Course">
<id column="course_id" property="courseId"/>
<result column="course_name" property="courseName"/>
<collection property="students" ofType="com.ebay.pojo.Students">
<id column="sid" property="stuId"/>
<result column="sname" property="stuName"/>
<result column="sage" property="stuAge"/>
</collection>
</resultMap>
<select id="queryStudentByCourse" resultMap="courseMap">
SELECT * FROM courses c INNER JOIN grades g INNER JOIN students s
ON c.course_id=g.cid and g.sid=s.sid
where c.course_id=#{courseId}
</select>
八、动态SQL
1.什么是动态SQL?
根据查询条件动态完成SQL的拼接
2.动态SQL使用案例
2.1 创建数据表
CREATE TABLE members(
member_id int PRIMARY KEY auto_increment,
member_nick varchar(20) not null UNIQUE,
member_gender char(2) not null,
member_age int not null,
member_city varchar(30) not null
)
2.2创建实体类
Member.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Member {
private Integer memberId;
private String memberNick;
private String memberGender;
private Integer memberAge;
private String memberCity;
}
2.3创建DAO
MemberDAO.java
//在多条件查询中,如果查询条件不确定,可以使用HashMap做参数
List searchMember(HashMap<String,Object> params);
//也可以定义专门用于存放查询条件的实体类存放参数
List searchMember(MemberSearchCondition memberSearchCondition);
public interface MemberDAO {
List<Member> searchMember(Member member);
}
2.4动态SQL使用
MemberMapper.xml
2.4.1 if
<resultMap id="memberMap" type="com.ebay.pojo.Member">
<id column="member_id" property="memberId"/>
<result column="member_nick" property="memberNick"/>
<result column="member_gender" property="memberGender"/>
<result column="member_age" property="memberAge"/>
<result column="member_city" property="memberCity"/>
</resultMap>
<select id="searchMember" resultMap="memberMap">
select * from members
where 1=1
-- memberNick参数对象的属性/参数map的key
<if test="memberNick !=null">
and member_nick=#{memberNick}
</if>
<if test="memberAge !=null">
and member_age=#{memberAge}
</if>
</select>
2.4.2 where
<select id="searchMember" resultMap="memberMap">
select * from members
<where>
-- memberNick参数对象的属性/参数map的key
<if test="memberNick !=null">
and member_nick=#{memberNick}
</if>
<if test="memberAge !=null">
and member_age=#{memberAge}
</if>
</where>
</select>
2.4.3 trim
select * from members
<trim prefix="where" prefixOverrides="and|or" suffix="order by member_age">
<if test="memberNick !=null">
and member_nick=#{memberNick}
</if>
<if test="memberAge !=null">
and member_age=#{memberAge}
</if>
</trim>
2.4.4 foreach
Member.java
List<Member> searchMemberByCity(List<String> cities);
MemberMapper.xml
<select id="searchMemberByCity" resultMap="memberMap">
select * from members where member_city
in
<foreach collection="list" item="cities" separator="," open="(" close=")">
#{cities}
</foreach>
</select>
九、模糊查询
1.模糊查询实现
Member.java
List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
MemberMapper.xml
<select id="searchMemberByNick" resultMap="memberMap" parameterType="java.lang.String">
select * from members where member_nick like '%${keyWord}%'
</select>
2.#与$的区别
- #{key} 表示获取参数,先完成sql编译(预编译),预编译之后再将获取的参数设置sql中
- ${key} 表示获取参数,先获取参数的值拼接到sql语句中,再编译执行sql语句
十、Mybatis日志配置
Mybatis作为一个封装好的ORM框架,其运行过程我们没法跟踪,为了让开发者了解Mybatis执行流程及每个执行步骤所完成的工作,Mybatis框架本身继承了log4j日志框架,对运行的过程进行跟踪记录。我们只需对Mybatis进行相关的日志配置,就可以看到Mybatis运行过程中的日志信息。
1.添加依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.添加日志配置文件
- 在resources目录下创建名为
log4j.properties
文件 - 在
log4j.properties
文件配置日志输出的方式
# 声明日志的输出级别,及输出方式
# ERROR WARN INFO DEBUG
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式 %t:表示线程名称 %5p:日志级别 %msg:日志信息 %m%n:日志换行
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %msg %m%n
3.日志信息的级别
在使用日志框架输出日志信息的时候,会根据输出的日志信息的重要程度分为5个级别
级别 | 说明 |
---|---|
DEBUG | 输出调试信息 |
INFO | 输出提示信息 |
WARN | 输出警告信息 |
ERROR | 一般性错误信息 |
FATAL | 致命性错误信息 |
十一、配置数据库连接池-整合Druid
Mybatis作为一个ORM框架,在进行数据库操作时时需要和数据库建立连接,Mybatis支持基于数据库连接池的连接创建方式。
当我们配置Mybatis数据源时,只要配置了dataSource标签的属性值type属性值为POOLED时,就可以使用Mybatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需进行自定义配置。
1.常见的连接池
- DBCP
- C3P0
- Druid
- Hikari
2.添加Druid的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
3.创建Druid连接池工厂
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory() {
this.dataSource=new DruidDataSource();
}
}
4.将DruidDataSourceFactory配置给Mybatis数据源
<transactionManager type="JDBC"/>
<!-- POOLED使用Mybatis内置连接池实现的-->
<!-- Mybatis需要一个连接池工厂,这个工厂可以产生数据库连接池PooledDataSourceFactory-->
<dataSource type="com.ebay.utils.DruidDataSourceFactory">
<!--数据库的驱动类名-->
<property name="driverClass" value="${mysql_driver}"/>
<!--连接数据库的url字符串-->
<property name="jdbcUrl" value="${mysql_url}"/>
<!--访问数据库的用户名-->
<property name="username" value="${mysql_username}"/>
<!--密码-->
<property name="password" value="${mysql_password}"/>
</dataSource>
十二、Mybatis缓存
Mybatis是基于JDBC的封装,使数据量操作更加便捷;Mybatis除了对JDBC操作步骤进行封装之外也对其性能进行了优化:
- 在Mybatis引入了缓存机制,用于提升Mybatis的检索效率
- 在Mybatis引入了延迟加载机制,用于减少对数据库不必要的访问
1.缓存的工作原理
缓存,就是存储数据的内存
Mybatis缓存分为一级缓存和二级缓存
2.一级缓存
一级缓存也叫做SqlSession级缓存,为每个SqlSession单独分配的缓存内存,无需手动开启可直接使用;多个SqlSession的缓存是不共享的。
特性:
1、如果多次查询使用的是同一个SqlSession对象,则第一次查询之后数据会存放到缓存,后续的查询则直接访问缓存中存储的数据
2、如果第一次查询完成之后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致;
3、当我们进行在查询时想要跳过缓存直接查询数据库,则可以通过
sqlSession.clearCache();
来清除当前sqlSession的缓存4、如果第一次查询之后第二次查询之前,使用当前sqlSession执行了修改操作,此修改操作会使第一次查询缓存的数据失效,因此第二次查询会再次访问数据库。
测试代码:
public void searchMemberByNick() {
SqlSession sqlSession = MybatisUtil.getSqlSession(false);
MemberDAO memberDAO1 = sqlSession.getMapper(MemberDAO.class);
List<Member> list = memberDAO1.searchMemberByNick("黄");
System.out.println(list);
System.out.println("_-----------------------------------------");
list.get(0).setMemberAge(66);
//sqlSession.clearCache();清除缓存
MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
List<Member> list1 = memberDAO2.searchMemberByNick("黄");
System.out.println(list1);
}
两次查询与数据库数据不一致问题
3.二级缓存
二级缓存也称为SqlSessionFactory级缓存,通过同一个factory对象获取的SqlSession可以共享二级缓存;在应用服务器中SqlSessionFactory是单例的,因此我们二级缓存可以实现全局共享。
特性:
1、二级缓存默认没有开启,需要在
mybatis-config.xml
的settting标签开启2、二级缓存只能缓存实现序列化接口的对象
- 在mybatis-config.xml开启使用二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 在需要使用二级缓存的Mapper文件中配置cache
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
eviction:可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU
flushInterval(刷新间隔):属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目):属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读):属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false
- 被缓存的实体类实现序列化接口
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Member implements Serializable {
private Integer memberId;
private String memberNick;
private String memberGender;
private Integer memberAge;
private String memberCity;
}
- 测试
@Test
public void searchMemberByNick() {
SqlSessionFactory factory = MybatisUtil.getSqlSessionFactory();
//1.多个SqlSession对象必须来自与同一个SqlSessionFactory
SqlSession sqlSession1 = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
MemberDAO memberDAO1 = sqlSession1.getMapper(MemberDAO.class);
List<Member> list = memberDAO1.searchMemberByNick("黄");
System.out.println(list);
//2.第一次查询之后执行sqlSession1.commit();会将当前SqlSession查询结果缓存到二级缓存
sqlSession1.commit();
System.out.println("_-----------------------------------------");
MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class);
List<Member> list1 = memberDAO2.searchMemberByNick("黄");
System.out.println(list1);
}
查询操作的缓存开关
useCache=“false”
<select id="searchMemberByNick" resultMap="memberMap" useCache="false">
select * from members where member_nick like '%${keyWord}%'
</select>
十三、延迟加载
延迟加载——如果在Mybatis开启了延迟加载,在执行子查询(至少查询两次及以上)时,默认只执行第一次查询,当用到子查询结果时,才会触发子查询的执行,如果无需使用子查询结果,则子查询不会执行。
fetchType=“lazy”
<association property="detail" select="com.ebay.dao.DetailDAO.queryDetail" column="user_id" fetchType="lazy">
</association>