Mybatis学习总结二
0、不需要修改的文件(在src下面)
jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis
jdbc.username=root
jdbc.password=3306
log4j.properties文件
log4j.rootLogger=DEBUG, Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.org.apache=INFO
SqlMapConfig.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>
<!-- 引入外部文件 -->
<properties resource="jdbc.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>
<!-- 加载UserMapper.xml文件 -->
<mappers>
<package name="UserMapper.xml" />
</mappers>
</configuration>
1、高级结果映射
1.1、一对一
数据表内容
开发环境
Address类
package com.csa.po;
public class Address {
private Integer addressId;
private String province;
private String city;
// get/set...方法
// toString方法
}
User类
package com.csa.po;
public class User {
private Integer userId;
private String name;
private Integer age;
private Address address;
// get/set...方法
// toString方法
}
UserMapper(Dao)接口
package com.csa.dao;
import com.csa.po.User;
public interface UserDao {
/**
* 通过用户id,查找User
* @param id
* @return
*/
public User selectUserById(Integer 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.csa.dao.UserDao">
<resultMap type="com.csa.po.User" id="User">
<!-- user信息 -->
<id column="user_id" property="userId"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<!-- 一对一 -->
<association property="address" javaType="com.csa.po.Address">
<id column="address_id" property="addressId"/>
<result column="province" property="province"/>
<result column="city" property="city"/>
</association>
</resultMap>
<!-- 查询用户,通过用户id -->
<select id="selectUserById" parameterType="int" resultMap="User">
select * from user,address
where user_id = #{userId} and address = address_id
</select>
</mapper>
测试
package com.csa.app;
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 com.csa.dao.UserDao;
import com.csa.po.User;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.selectUserById(3);
System.out.println(user);
// 关闭sqlSession
sqlSession.close();
}
}
测试结果
1.2、一对多
数据库内容
开发环境
Address类
package com.csa.po;
public class Address {
private Integer addressId;
private String province;
private String city;
// get/set...方法
// toString方法
}
Phone类
package com.csa.po;
public class Phone {
private Integer phoneId;
private String model;
private String memory;
// get/set...方法
// toString方法
}
User类
package com.csa.po;
import java.util.List;
public class User {
private Integer userId;
private String name;
private Integer age;
private Address address;
private List<Phone> phoneList;
// get/set...方法
// toString方法
}
UserMapper接口
package com.csa.dao;
import com.csa.po.User;
public interface UserMapper {
/**
* 通过用户id,查找User
* @param id
* @return
*/
public User selectUserById(Integer 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.csa.dao.UserMapper">
<resultMap type="com.csa.po.User" id="User">
<!-- user信息 -->
<id column="user_id" property="userId"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<!-- 一对一 -->
<association property="address" javaType="com.csa.po.Address">
<id column="address_id" property="addressId"/>
<result column="province" property="province"/>
<result column="city" property="city"/>
</association>
<!-- 一对多 -->
<collection property="phoneList" ofType="com.csa.po.Phone">
<id column="phone_id" property="phoneId"/>
<result column="model" property="model"/>
<result column="memory" property="memory"/>
</collection>
</resultMap>
<!-- 查询用户,通过用户id -->
<select id="selectUserById" parameterType="int" resultMap="User">
select * from user,address,phone
where user_id = #{id}
and address = address_id
and user = user_id
</select>
</mapper>
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.csa.dao.UserMapper">
<resultMap type="com.csa.po.User" id="UserMap">
<!-- user信息 -->
<id column="user_id" property="userId"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<!-- 一对一 -->
<association property="address" javaType="com.csa.po.Address">
<id column="address_id" property="addressId"/>
<result column="province" property="province"/>
<result column="city" property="city"/>
</association>
</resultMap>
<!-- 使用到继承 -->
<resultMap type="com.csa.po.User" id="User" extends="UserMap">
<!-- 一对多 -->
<collection property="phoneList" ofType="com.csa.po.Phone">
<id column="phone_id" property="phoneId"/>
<result column="model" property="model"/>
<result column="memory" property="memory"/>
</collection>
</resultMap>
<!-- 查询用户,通过用户id -->
<select id="selectUserById" parameterType="int" resultMap="User">
select * from user,address,phone
where user_id = #{id}
and address = address_id
and user = user_id
</select>
</mapper>
测试
package com.csa.app;
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 com.csa.dao.UserMapper;
import com.csa.po.User;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUserById(3);
System.out.println(user);
// 关闭sqlSession
sqlSession.close();
}
}
测试结果
1.3、多对多
1.3.0、不变的东西
数据库信息
开发环境
Course类
package com.csa.po;
import java.util.List;
public class Course {
private Integer courseId;
private String courseName;
private Integer credit;
private List<Student> studentList;
// get/set...方法
// toString方法
}
Student类
package com.csa.po;
import java.util.List;
public class Student {
private Integer studentId;
private String studentName;
private Integer age;
private List<Course> courseList;
// get/set...方法
// toString方法
}
SqlMapConfig.xml的一些小改变
<!-- 加载UserMapper.xml文件 -->
<mappers>
<mapper resource="StudentMapper.xml"/>
<mapper resource="CourseMapper.xml"/>
<mapper resource="StudentCourseMapper.xml"/>
</mappers>
1.3.1、查询学生所选课程的情况
StudentMapper.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.csa.dao.StudentMapper">
<!-- CURD操作 -->
<resultMap type="com.csa.po.Student" id="Student">
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="age" property="age"/>
<collection property="courseList" ofType="com.csa.po.Course">
<id column="course_id" property="courseId"/>
<result column="course_name" property="courseName"/>
<result column="credit" property="credit"/>
</collection>
</resultMap>
<!-- 通过id查找该学生选修的课程 -->
<select id="selectStudentCoursesById" parameterType="int" resultMap="Student">
select * from student natural join course natural join student_course
where student_id = #{studentId}
</select>
</mapper>
StudentMapper接口
package com.csa.dao;
import com.csa.po.Student;
public interface StudentMapper {
/**
* 通过学生id查找学生选修情况
* @param id
* @return
*/
public Student selectStudentCoursesById(Integer id);
}
测试
package com.csa.app;
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 com.csa.dao.StudentMapper;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
StudentMapper studentMapper = (StudentMapper) sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectStudentCoursesById(2);
System.out.println(student);
// 关闭sqlSession
sqlSession.close();
}
}
测试结果
1.3.2、查询本门课程的所有学生
CourseMapper.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.csa.dao.CourseMapper">
<!-- CRUD操作 -->
<resultMap type="com.csa.po.Course" id="Course">
<id column="course_id" property="courseId"/>
<result column="course_name" property="courseName"/>
<result column="credit" property="credit"/>
<collection property="studentList" ofType="com.csa.po.Student">
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="age" property="age"/>
</collection>
</resultMap>
<!-- 通过课程id查询本课程的所有学生 -->
<select id="selectCourseStudentsById" parameterType="int" resultMap="Course">
select * from student natural join course natural join student_course
where course_id = #{courseId}
</select>
</mapper>
CourseMapper接口
package com.csa.dao;
import com.csa.po.Course;
public interface CourseMapper {
/**
* 通过课程id查询本门课程的所有学生
* @param id
* @return
*/
public Course selectCourseStudentsById(Integer id);
}
测试
package com.csa.app;
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 com.csa.dao.CourseMapper;
import com.csa.dao.StudentMapper;
import com.csa.po.Course;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class);
Course course = courseMapper.selectCourseStudentsById(3);
System.out.println(course);
// 关闭sqlSession
sqlSession.close();
}
}
测试结果
1.3.3、增加一条选修记录
StudentCourseMapper.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.csa.dao.StudentCourseMapper">
<!-- CURD操作 -->
<insert id="addElective">
<!-- 没有给定参数则是为param1、param2...paramn -->
insert into student_course values(#{param1.studentId},#{param2.courseId},#{param3})
</insert>
</mapper>
StudentCourse接口
package com.csa.dao;
import com.csa.po.Course;
import com.csa.po.Student;
public interface StudentCourseMapper {
public void addElective(Student student,Course course,Integer score);
}
测试(记得提交事务)
package com.csa.app;
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 com.csa.dao.StudentCourseMapper;
import com.csa.po.Course;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
StudentCourseMapper studentCourseMapper = sqlSession.getMapper(StudentCourseMapper.class);
Student student = new Student();
student.setStudentId(2);
Course course = new Course();
course.setCourseId(2);
studentCourseMapper.addElective(student, course, 99);
// 提交事务
sqlSession.commit();
// 关闭sqlSession
sqlSession.close();
}
}
测试结果
2、延迟加载
2.1、什么是延迟加载
延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息。
在mybatis中,resultMap标签 的association标签和collection标签具有延迟加载的功能。
2.2、在SqlMapConfig.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>
<!-- 引入外部文件 -->
<properties resource="jdbc.properties"></properties>
<settings>
<!-- 开启延迟加载,默认为true -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载,默认为true -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 环境 -->
<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>
<!-- 加载UserMapper.xml文件 -->
<mappers>
<mapper resource="StudentMapper.xml"/>
<mapper resource="CourseMapper.xml"/>
</mappers>
</configuration>
2.3、一对多的延迟加载案例
数据库信息
开发环境
两个实体类
Student
package com.csa.po;
import java.util.List;
public class Student {
private Integer studentId;
private String studentName;
private Integer age;
private List<Course> courseList;
// get/set...方法
// toString方法
}
Course
package com.csa.po;
import java.util.List;
public class Course {
private Integer courseId;
private String courseName;
private Integer credit;
private List<Student> studentList;
// get/set...方法
// toString方法
}
两个接口
StudentMapper
package com.csa.dao;
import com.csa.po.Student;
public interface StudentMapper {
/**
* 通过学生id查找学生选修情况
* @param id
* @return
*/
public Student selectStudentCoursesById(Integer id);
}
CourseMapper
package com.csa.dao;
import com.csa.po.Course;
public interface CourseMapper {
/**
* 通过课程id查询本门课程的所有学生
* @param id
* @return
*/
public Course selectCourseStudentsById(Integer id);
}
两个xml配置(重点)
StudentMapper.xml(注意sql语句和resultMap)
<?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.csa.dao.StudentMapper">
<!-- CURD操作 -->
<resultMap type="com.csa.po.Student" id="Student">
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="age" property="age"/>
<collection property="courseList" ofType="com.csa.po.Course"
select="com.csa.dao.CourseMapper.selectCourseStudentsById" column="student_id"></collection>
</resultMap>
<!-- 通过id查找该学生选修的课程 -->
<select id="selectStudentCoursesById" parameterType="int" resultMap="Student">
select * from student
where student_id = #{studentId}
</select>
</mapper>
CourseMapper.xml(注意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">
<mapper namespace="com.csa.dao.CourseMapper">
<!-- CRUD操作 -->
<resultMap type="com.csa.po.Course" id="Course">
<id column="course_id" property="courseId"/>
<result column="course_name" property="courseName"/>
<result column="credit" property="credit"/>
<collection property="studentList" ofType="com.csa.po.Student">
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="age" property="age"/>
</collection>
</resultMap>
<!-- 通过课程id查询本课程的所有学生 -->
<select id="selectCourseStudentsById" parameterType="int" resultMap="Course">
select * from course natural join student_course
where student_id = #{student_id}
</select>
</mapper>
测试
不加载课程信息
package com.csa.app;
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 com.csa.dao.StudentMapper;
import com.csa.po.Course;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectStudentCoursesById(2);
// 将下面这段放开,则是延迟加载了course!
/*for (Course course : student.getCourseList()) {
System.out.println(course);
}*/
// 关闭sqlSession
sqlSession.close();
}
}
加载课程信息
package com.csa.app;
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 com.csa.dao.StudentMapper;
import com.csa.po.Course;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectStudentCoursesById(2);
for (Course course : student.getCourseList()) {
System.out.println(course);
}
// 关闭sqlSession
sqlSession.close();
}
}
测试结果
不加载课程信息结果
加载课程信息结果(当属性被访问时,再去数据库查找,于是mapper需要这样的sql语句)
# 这里的查询条件是学号!
select * from course natural join student_course
where student_id = #{student_id}
3、查询缓存
3.0、什么是缓存
Mybatis的缓存,包括一级缓存和二级缓存。
一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。
二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。
3.1、一级缓存
一级缓存是默认使用的。同一个sqlsession中的缓存。
开发环境
实体类
package com.csa.po;
public class Student {
private Integer studentId;
private String studentName;
private Integer age;
// get/set...方法
// toString方法
}
接口
package com.csa.dao;
import com.csa.po.Student;
public interface StudentMapper {
/**
* 通过学生id查找学生
* @param id
* @return
*/
public Student selectStudentById(Integer id);
}
配置文件
<?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.csa.dao.StudentMapper">
<!-- CURD操作 -->
<resultMap type="com.csa.po.Student" id="Student">
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="age" property="age"/>
</resultMap>
<!-- 通过id查找该学生选修的课程 -->
<select id="selectStudentById" parameterType="int" resultMap="Student">
select * from student
where student_id = #{studentId}
</select>
</mapper>
测试
package com.csa.app;
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 com.csa.dao.StudentMapper;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建dao
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student1 = studentMapper.selectStudentById(2);
Student student2 = studentMapper.selectStudentById(2);
System.out.println(student1);
System.out.println(student2);
// 关闭sqlSession
sqlSession.close();
}
}
测试结果(可以看出,只做了一次查询)
3.2、二级缓存
二级缓存需要手动开启。
二级缓存原理
开发环境
开启缓存
SqlMapConfig.xml配置
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
StudentMapper.xml配置
<!-- 开启二级缓存,默认使用了PerpettualCache -->
<cache/>
实体类
需要序列化!
package com.csa.po;
import java.io.Serializable;
public class Student implements Serializable{
private Integer studentId;
private String studentName;
private Integer age;
// get/set...方法
// toString方法
}
接口
与上面一样!
测试
package com.csa.app;
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 com.csa.dao.StudentMapper;
import com.csa.po.Student;
public class App {
public static void main(String[] args) throws Exception {
// 读取全局配置文件
String resource = "SqlMapConfig.xml";
InputStream input = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 创建sqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
// 创建dao
StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class);
Student student1 = studentMapper1.selectStudentById(2);
// 关闭sqlSession
sqlSession1.close();
Student student2 = studentMapper2.selectStudentById(2);
sqlSession2.close();
Student student3 = studentMapper3.selectStudentById(2);
Student student11 = studentMapper3.selectStudentById(4);
sqlSession3.close();
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
System.out.println(student11);
}
}
测试结果
禁用缓存与刷新缓存
<!--
useCache:是否使用缓存
flushCache:是否刷新缓存
-->
<!-- 通过id查找该学生选修的课程 -->
<select id="selectStudentById" parameterType="int" resultMap="Student"
useCache="false" flushCache="true">
select * from student
where student_id = #{studentId}
</select>
3.3、整合ehcache
应用场景
使用场景:对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。
注意:在使用二级缓存的时候,要设置一下刷新间隔(cache标签中有一个flashInterval属性)来定时刷新二级缓存,这个刷新间隔根据具体需求来设置,比如设置30分钟、60分钟等,单位为毫秒。
导包
添加ehcache.xml文件(下面的diskStore是我们自己的缓存路径)
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!--
The ehcache-failsafe.xml is a default configuration for ehcache, if an ehcache.xml is not configured.
The diskStore element is optional. It must be configured if you have overflowToDisk or diskPersistent enabled
for any cache. If it is not configured, a warning will be issues and java.io.tmpdir will be used.
diskStore has only one attribute - "path". It is the path to the directory where .data and .index files will be created.
If the path is a Java System Property it is replaced by its value in the
running VM.
The following properties are translated:
* user.home - User's home directory
* user.dir - User's current working directory
* java.io.tmpdir - Default temp file path
* ehcache.disk.store.dir - A system property you would normally specify on the command line
e.g. java -Dehcache.disk.store.dir=/u01/myapp/diskdir ...
Subdirectories can be specified below the property e.g. java.io.tmpdir/one
-->
<diskStore path="java.io.tmpdir"/>
<!--
Specifies a CacheManagerEventListenerFactory, be used to create a CacheManagerPeerProvider,
which is notified when Caches are added or removed from the CacheManager.
The attributes of CacheManagerEventListenerFactory are:
* class - a fully qualified factory class name
* properties - comma separated properties having meaning only to the factory.
Sets the fully qualified class name to be registered as the CacheManager event listener.
The events include:
* adding a Cache
* removing a Cache
Callbacks to listener methods are synchronous and unsynchronized. It is the responsibility
of the implementer to safely handle the potential performance and thread safety issues
depending on what their listener is doing.
If no class is specified, no listener is created. There is no default.
<cacheManagerEventListenerFactory class="" properties=""/>
-->
<!--
(Enable for distributed operation)
Specifies a CacheManagerPeerProviderFactory which will be used to create a
CacheManagerPeerProvider, which discovers other CacheManagers in the cluster.
The attributes of cacheManagerPeerProviderFactory are:
* class - a fully qualified factory class name
* properties - comma separated properties having meaning only to the factory.
Ehcache comes with a built-in RMI-based distribution system with two means of discovery of
CacheManager peers participating in the cluster:
* automatic, using a multicast group. This one automatically discovers peers and detects
changes such as peers entering and leaving the group
* manual, using manual rmiURL configuration. A hardcoded list of peers is provided at
configuration time.
Configuring Automatic Discovery:
Automatic discovery is configured as per the following example:
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=32"/>
Valid properties are:
* peerDiscovery (mandatory) - specify "automatic"
* multicastGroupAddress (mandatory) - specify a valid multicast group address
* multicastGroupPort (mandatory) - specify a dedicated port for the multicast heartbeat
traffic
* timeToLive - specify a value between 0 and 255 which determines how far the packets will propagate.
By convention, the restrictions are:
0 - the same host
1 - the same subnet
32 - the same site
64 - the same region
128 - the same continent
255 - unrestricted
Configuring Manual Discovery:
Manual discovery is configured as per the following example:
<cacheManagerPeerProviderFactory class=
"net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server1:40000/sampleCache1|//server2:40000/sampleCache1
| //server1:40000/sampleCache2|//server2:40000/sampleCache2"/>
Valid properties are:
* peerDiscovery (mandatory) - specify "manual"
* rmiUrls (mandatory) - specify a pipe separated list of rmiUrls, in the form
//hostname:port
The hostname is the hostname of the remote CacheManager peer. The port is the listening
port of the RMICacheManagerPeerListener of the remote CacheManager peer.
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic,
multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=1"/>
-->
<!--
(Enable for distributed operation)
Specifies a CacheManagerPeerListenerFactory which will be used to create a
CacheManagerPeerListener, which
listens for messages from cache replicators participating in the cluster.
The attributes of cacheManagerPeerListenerFactory are:
class - a fully qualified factory class name
properties - comma separated properties having meaning only to the factory.
Ehcache comes with a built-in RMI-based distribution system. The listener component is
RMICacheManagerPeerListener which is configured using
RMICacheManagerPeerListenerFactory. It is configured as per the following example:
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=fully_qualified_hostname_or_ip,
port=40001,
socketTimeoutMillis=120000"/>
All properties are optional. They are:
* hostName - the hostName of the host the listener is running on. Specify
where the host is multihomed and you want to control the interface over which cluster
messages are received. Defaults to the host name of the default interface if not
specified.
* port - the port the listener listens on. This defaults to a free port if not specified.
* socketTimeoutMillis - the number of ms client sockets will stay open when sending
messages to the listener. This should be long enough for the slowest message.
If not specified it defaults 120000ms.
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
-->
<!-- Cache configuration.
The following attributes are required.
name:
Sets the name of the cache. This is used to identify the cache. It must be unique.
maxElementsInMemory:
Sets the maximum number of objects that will be created in memory (0 == no limit)
maxElementsOnDisk:
Sets the maximum number of objects that will be maintained in the DiskStore
The default value is zero, meaning unlimited.
eternal:
Sets whether elements are eternal. If eternal, timeouts are ignored and the
element is never expired.
overflowToDisk:
Sets whether elements can overflow to disk when the in-memory cache
has reached the maxInMemory limit.
The following attributes are optional.
timeToIdleSeconds:
Sets the time to idle for an element before it expires.
i.e. The maximum amount of time between accesses before an element expires
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that an Element can idle for infinity.
The default value is 0.
timeToLiveSeconds:
Sets the time to live for an element before it expires.
i.e. The maximum time between creation time and when an element expires.
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that and Element can live for infinity.
The default value is 0.
diskPersistent:
Whether the disk store persists between restarts of the Virtual Machine.
The default value is false.
diskExpiryThreadIntervalSeconds:
The number of seconds between runs of the disk expiry thread. The default value
is 120 seconds.
diskSpoolBufferSizeMB:
This is the size to allocate the DiskStore for a spool buffer. Writes are made
to this area and then asynchronously written to disk. The default size is 30MB.
Each spool buffer is used only by its cache. If you get OutOfMemory errors consider
lowering this value. To improve DiskStore performance consider increasing it. Trace level
logging in the DiskStore will show if put back ups are occurring.
memoryStoreEvictionPolicy:
Policy would be enforced upon reaching the maxElementsInMemory limit. Default
policy is Least Recently Used (specified as LRU). Other policies available -
First In First Out (specified as FIFO) and Less Frequently Used
(specified as LFU)
Cache elements can also contain sub elements which take the same format of a factory class
and properties. Defined sub-elements are:
* cacheEventListenerFactory - Enables registration of listeners for cache events, such as
put, remove, update, and expire.
* bootstrapCacheLoaderFactory - Specifies a BootstrapCacheLoader, which is called by a
cache on initialisation to prepopulate itself.
Each cache that will be distributed needs to set a cache event listener which replicates
messages to the other CacheManager peers. For the built-in RMI implementation this is done
by adding a cacheEventListenerFactory element of type RMICacheReplicatorFactory to each
distributed cache's configuration as per the following example:
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=true,
replicateRemovals=true "/>
The RMICacheReplicatorFactory recognises the following properties:
* replicatePuts=true|false - whether new elements placed in a cache are
replicated to others. Defaults to true.
* replicateUpdates=true|false - whether new elements which override an
element already existing with the same key are replicated. Defaults to true.
* replicateRemovals=true - whether element removals are replicated. Defaults to true.
* replicateAsynchronously=true | false - whether replications are
asynchronous (true) or synchronous (false). Defaults to true.
* replicateUpdatesViaCopy=true | false - whether the new elements are
copied to other caches (true), or whether a remove message is sent. Defaults to true.
* asynchronousReplicationIntervalMillis=<number of milliseconds> - The asynchronous
replicator runs at a set interval of milliseconds. The default is 1000. The minimum
is 10. This property is only applicable if replicateAsynchronously=true
* asynchronousReplicationMaximumBatchSize=<number of operations> - The maximum
number of operations that will be batch within a single RMI message. The default
is 1000. This property is only applicable if replicateAsynchronously=true
The RMIBootstrapCacheLoader bootstraps caches in clusters where RMICacheReplicators are
used. It is configured as per the following example:
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
The RMIBootstrapCacheLoaderFactory recognises the following optional properties:
* bootstrapAsynchronously=true|false - whether the bootstrap happens in the background
after the cache has started. If false, bootstrapping must complete before the cache is
made available. The default value is true.
* maximumChunkSizeBytes=<integer> - Caches can potentially be very large, larger than the
memory limits of the VM. This property allows the bootstraper to fetched elements in
chunks. The default chunk size is 5000000 (5MB).
-->
<!--
Mandatory Default Cache configuration. These settings will be applied to caches
created programmtically using CacheManager.add(String cacheName)
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
修改StudentMapper.xml
<!-- 开启二级缓存,默认使用了org.mybatis.caches.ehcache.EncacheCache -->
<cache type="org.mybatis.caches.ehcache.EncacheCache"/>
4、逆向工程(发送一波黑马笔记)
4.1、下载逆向工程
4.2、创建逆向工程
4.3、创建Generator.java
4.4、添加generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
password="root">
</jdbcConnection>
<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg"
userId="yycg" password="yycg"> </jdbcConnection> -->
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL
和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.itheima.ms.po"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.itheima.ms.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.itheima.ms.mapper" targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="items"></table>
<table tableName="orders"></table>
<table tableName="orderdetail"></table>
<table tableName="user"></table>
</context>
</generatorConfiguration>
4.5、注意事项
Mapper.xml文件已经存在时,如果进行重新生成则mapper.xml文件时,内容不被覆盖而是进行内容追加,结果导致mybatis解析失败。
解决方法:删除原来已经生成的mapper xml文件再进行生成。
Mybatis自动生成的po及mapper.java文件不是内容而是直接覆盖没有此问题。
参考
黑马笔记、黑马、黑马!!!