<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
例如:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
<?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/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
public class Student {
private Integer SID;//学生ID
private String Sname;//学生姓名
private String Ssex;//学生性别
private Integer Sage;//学生年龄
//省略getter和setter方法
}
public interface StudentMapper {
Student selectStudentById(Integer id);//通过学生的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">
<!--根标签 namespace命令空间:给定接口文件的全路径-->
<mapper namespace="com.example.springBootDemo.TestMabits.mapper.StudentMapper">
<!--
当ResultType无法完成某些字段映射时,需要resultMap进行显性映射
resultMap标签主要用来显性指定返回映射关系
id:必填,表示去名称,可以随便取
type:必填,指定显性映射的java类的全限定名
id标签:指定主键映射关系,ID标签一般使用一次指定主键即可
result:指定非主属性映射关系,可以多次使用
property:指定java类中属性名(必填
column:指定数据库中的属性名称(必填
jdbcType:指定数据库中当前属性类型(选填
typeHandler:如果是有自定义类型处理器需要在这里指定自定义类型处理器的
-->
<resultMap id="studentMap" type="student">
<id property="SID" column="SID"/>
<result property="Sname" column="Sname"/>
<result property="Ssex" column="Ssex"/>
<result property="Sage" column="Sage"/>
</resultMap>
<!--
select标签是查询操作标签
id属性:(必填的)Statement的id,必填的,和接口文件中的方法名保持一致
parameterType:表述输入参数的类型(String,pojo,Integer)
resultType:(必填)表示输出参数的类型(String,pojo,Integer)还可以返回resultMap类型(返回的是hashmap)两种类型二选一
#{XXX}:表示占位符 XXX:接口方法中的参数名称
-->
<select id="selectStudentById" parameterType="int" resultType="com.example.springBootDemo.TestMabits.pojo.StudentA">
select * from Student where SID =#{sid}
</select>
<!-- id:在空间命名具有唯一性,和方法名保持一致
parameterType:指定入参的类型,可以是类的全限定名或者
-->
<insert id="insertStudent" flushCache="true" parameterType="student"/>
<select id="selectAllStudents" resultType="com.example.springBootDemo.TestMabits.pojo.Student">
select * from Student;
</select>
</mapper>
<!--映射文件-->
<mappers>
<!--resource属性,指定mapper.xml文件的路径-->
<mapper resource="mapper/StudentMapper.xml"/>
</mappers>
package com.example.springBootDemo.TestMabits;
import com.example.springBootDemo.TestMabits.mapper.StudentMapper;
import com.example.springBootDemo.TestMabits.pojo.Student;
import org.apache.ibatis.annotations.Delete;
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;
import java.util.List;
import java.util.Properties;
public class MybatisDemo {
public static void main(String[] args) throws IOException {
//1、创建SQLSessionFactory对象
//1.读取全局配置文件
String path="mybatis_config.xml";
//通过Resource获取文件流
InputStream resourceAsStream = Resources.getResourceAsStream(path);
//创建会话工厂,通过SQLSessionFactoryBUilder来创建
Properties properties = new Properties();
properties.setProperty("username","root");
properties.setProperty("password","123456");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream,properties);
// 创建会话,会话工厂是用来读取全局配置文件,通过builder类来创建
// 通过读取配置文件来创建实例,一般配置文件读取依次即可
// 对数据库CRUD操作
// SQLSession是线程不安全的,将其设置为方法的局部变量
// 以及缓存是基于SQLSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过代理模式创建代理类
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
// Student student = studentMapper.selectStudentById(4);
// System.out.println(student);
List<Student> students=studentMapper.selectAllStudents();
for (Student s:students) {
System.out.println(s);
}
}
}
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
<!--类型别名-->
<typeAliases>
<!--单个类型定义别名: type:pojo全路径 alias:别名的名称-->
<typeAlias type="com.tulun.Mybatis.pojo.Student" alias="student"/>
<!--批量的别名定义 package:指定包名,将包下的所有pojo类定义别名,别名是类型(首字母大写或者小写都可以)-->
<package name="com.tulun.Mybatis.pojo"/>
</typeAliases>
<mappers>
<!--单个文件映射:resource属性一次加载一个文件,
指定xml文件位置,通过namespace来查找mapper接口文件-->
<mapper resource="mapper/StudentMapper.xml"/>
<!--class方式映射:通过mapper接口映射单个文件,
遵循规则:mapper.java和mapper.xml放在同一个目录下,且命名相同-->
<!--<mapper class="com.tulun.Mybatis.mapper.StudentMapper"/>-->
<!--批量mapper的接口扫描规则
遵循规则:mapper.java和mapper.xml放在同一个目录下,且命名相同-->
<!--<package name="com.tulun.Mybatis.mapper"/>-->
</mappers>
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
<!--
查询操作是select标签
id属性:(必填的)Statement的id是唯一标识,和接口文件中的方法名保持一致
parameterType:表示输入参数的类型(Pojo,Integer,String...)入参类型也可以是parameterMap(hashMap)两种类型二选1
resultType(必填):表示输出参数的类型(Pojo,Integer,String) 返回类型也可以是resultMap(hashMap)两种类型二选1
#{XXX}:表示占位符 XXX:接口方法中的参数名称
-->
<select id="selectStudentById" parameterType="int" rresultType="student">
select * from student where SID =#{sid}
</select>
<!--
插入操作是insert标签
id属性:(必填的)Statement的id是唯一标识,和接口文件中的方法名保持一致
parameterType:(可选操作)表示输入参数的类型(Pojo,Integer,String...)入参类型也可以是parameterMap(hashMap)两种类型二选1
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo类属性名
-->
<insert id="testInsert" parameterMap="" useGeneratedKeys="true" keyColumn="" keyProperty=""
<!--
变更操作是update标签
id属性:(必填的)Statement的id是唯一标识,和接口文件中的方法名保持一致
parameterType:(可选操作)表示输入参数的类型(Pojo,Integer,String...)入参类型也可以是parameterMap(hashMap)两种类型二选1
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo类属性名
-->
<update id="testUpdate" >
</update>
<!--
删除操作是delete标签
id属性:(必填的)Statement的id是唯一标识,和接口文件中的方法名保持一致
parameterType:(可选操作)表示输入参数的类型(Pojo,Integer,String...)入参类型也可以是parameterMap(hashMap)两种类型二选1
-->
<delete id="testdelete">
</delete>
MyBatis的强大之处在于自定义SQL语句,映射器的xml文件方式相比JDBC简单,节省代码量
public interface StudentMapper {
Student selectStudentById(Integer sid);
}
<?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 namespace="com.tulun.Mybatis.mapper.StudentMapper">
<select id="selectStudentById" parameterType="int" resultType="student">
select * from student where SID =#{sid}
</select>
</mapper>
<mappers>
<mapper resource="mapper/StudentMapper.xml"/>
</mappers>
<!--
当resultType无法完成某些自动字段映射时,需要resultMap进行显性映射关系配置
resultMap标签主要是用来显性指定返回值映射关系
id属性:必填,表示取名称,可以随便给定
type属性:必填,指定显性映射的Java类的全限定名
id标签:指定主键映射关系 id标签一般使用一次指定主键即可
result标签:指定非主属性映射关系,可以多次使用
property属性:指定Java类中属性名(必填)
column属性:指定数据库中的属性名称(必填)
jdbcType属性:指定数据库中当前属性类型(选填)
javaType属性:指定Java类中属性类型(选填)
typeHandler属性:类型Handler,如果是有自定义类型处理器需要在这里指定自定义类型处理器的全限定名(选填)
-->
<resultMap id="studentMap" type="student">
<id property="SID" column="SID" />
<result property="name" column="Sname" />
<result property="Ssex" column="Ssex" />
<result property="Sage" column="Sage" />
</resultMap>
select标签是查询操作标签
id属性:(必填的)Statement的id,必填的,和接口文件中的方法名保持一致
parameterType:表述输入参数的类型(String,pojo,Integer)
resultType:(必填)表示输出参数的类型(String,pojo,Integer)还可以返回resultMap类型(返回的是hashmap)两种类型二选一
#{XXX}:表示占位符 XXX:接口方法中的参数名称
<!--
insert标签表示插入数据
id:是Statementid,在命名空间具有唯一性,和方法名保持一致
parameterType/parameterType属性:指定入参的类型,可以是类的全限定名或者别名
flushCache属性:参数为true或者false 默认为true ,用来清除一级缓存和二级缓存
keyProperty:指定pojo类中主键属性名
useGeneratedKeys属性:默认为false,可以是false或者是true,
如果是true,会采用数据自增主键 auto_increment 完整性约束
keyColumn:表示数据库表中主键的属性名,在update和insert中有效
没有返回类型指定,如果接口方法返回为int自动给定值
-->
<insert id="insertStudent" parameterType="student">
insert into
student(SID,Sname,Sage,Ssex)
values
(#{SID},#{name},#{Sage},#{Ssex})
</insert>
public class User {
private Integer id;
private String name;
private String passwd;
//省略getter和setter方法
}
public interface UserMapper {
}
<mapper class="com.tulun.Mybatis.mapper.UserMapper"/>
/**
* 通过ID查询
*/
@Select("select * from user where id = #{id}")
public User selectUserById(Integer id);
当字段无法映射是,通过@Results注解完成显性映射;
/**
* 在mybatis的早的版本:3.3.0之前版本@Results是不能共用
在mybatis的3.3.1之后的版本@Results 增加了id属性,可以通过id共用同一个Results结果
@Results和XML形式中resultMap标签类型
*/
@Results(id = "userMap", value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "name", column = "name"),
@Result(property = "pass", column = "passwd")
}
)
/**
* 插入数据
* @Insert表示插入
* @Insert和XML中的insert标签类似
* 采用数据库自增主键时使用@Options注解,其中有useGeneratedKeys属性,设置为true
*/
@Options(useGeneratedKeys = true,keyColumn = "id")
@Insert("insert into user(name,passwd) values(#{name},#{pass})")
int insertUser(User user);
@Update的两种情况
@Update("update Studet set sex='男' where id=#{id}")
public int update(int id);
这种情况只有一个参数,参数id可以不使用@Param;
@Update("update Student set sex=#{name} where id=#{id}")
public int update(@Param("id") int id,@Param("name") String name);
这种情况有两个参数,需要使用@Param进行两个参数的区分,否则,在操作数据表时会报出SQL语句的注入风险;
@Delete("delete from Student where id=#{id}")
int deleteStudentById(Int id);
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
<cache type="com.domain.something.MyCustomCache"/>
public interface Cache {
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
boolean hasKey(Object key);
Object removeObject(Object key);
void clear();
}
<cache type="com.domain.something.MyCustomCache">
<property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>
trim、if、where、set、foreach、choose、when、otherwise、bind
<select id="selectStudentsByNameOrSex" parameterType="student" resultMap="studentMap">
select * from student where 1=1
<if test="name != null" >
and Sname = #{name}
</if>
<if test="Ssex != null">
and Ssex = #{Ssex}
</if>
</select>
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student=new Student();
student.setSname("李小龙");
student.setSsex("男");
studentMapper.selectStudentsByNameOrSex(student);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student=new Student();
student.setSname("李小龙");
studentMapper.selectStudentsByNameOrSex(student);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student=new Student();
student.setSsex("男");
studentMapper.selectStudentsByNameOrSex(student);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student=new Student();
studentMapper.selectStudentsByNameOrSex(student);
<select id="selectStudentsByNameOrSex" parameterType="student" resultMap="studentMap">
select * from student
<where>
<if test="Sname != null" >
and Sname = #{Sname}
</if>
<if test="Ssex != null">
and Ssex = #{Ssex}
</if>
</where>
</select>
<select id="selectStudentsByNameOrSex" parameterType="student" resultMap="studentMap">
select * from student
<trim prefix="where" prefixOverrides="and">
<if test="name != null">
and Sname = #{name}
</if>
<if test="Ssex != null">
and Ssex = #{Ssex}
</if>
</trim>
</select>
List<Student> batchQueryStudent(List<Integer> ids);
select * from student where SID in(1,2,3,4,5);
<select id="batchQueryStudent" resultMap="studentMap">
select * from student where SID in
<foreach collection="list" item="id" open="(" close=")" separator="," >
#{id}
</foreach>
</select>
select * from student where Sname like “%L%”
List<Student> selectStudentByName(String name);
<select id="selectStudentByName" parameterType="string" resultMap="studentMap">
select * from student where Sname like #{name}
</select>
<select id="selectStudentByName" parameterType="string" resultMap="studentMap">
select * from student where Sname like concat('%',#{name},'%')
</select>
List<Student> selectStudentByName(Student student);
<select id="selectStudentByName" parameterType="student" resultMap="studentMap">
<bind name="ln" value="'%'+name+'%'"/>
select * from student where Sname like #{ln}
</select>
drop table if exists `items`;
CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '商品名称',
`price` float(10,1) NOT NULL COMMENT '商品定价',
`detail` text COMMENT '商品描述',
`pic` varchar(64) DEFAULT NULL COMMENT '商品图片',
`createtime` datetime NOT NULL COMMENT '生产日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Table structure for table `user` */
drop table if exists `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Table structure for table `orders` */
drop table if exists `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Table structure for table `orderdetail` */
drop table if exists `orderdetail`;
CREATE TABLE `orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orders_id` int(11) NOT NULL COMMENT '订单id',
`items_id` int(11) NOT NULL COMMENT '商品id',
`items_num` int(11) DEFAULT NULL COMMENT '商品购买数量',
PRIMARY KEY (`id`),
KEY `FK_orderdetail_1` (`orders_id`),
KEY `FK_orderdetail_2` (`items_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
insert into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'台式机',3000.0,'该电脑质量非常好!!!!',NULL,'2015-02-03 13:22:53'),(2,'笔记本',6000.0,'笔记本性能好,质量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'背包',200.0,'名牌背包,容量大质量好!!!!',NULL,'2015-02-06 13:23:02');
/*Data for the table `user` */
insert into `user`(`id`,`username`,`sex`,`address`) values (1,'王五','2',NULL),(10,'张三','1','北京市'),(16,'张小明','1','陕西西安'),(22,'陈小明','1','陕西西安'),(24,'张三丰','1','陕西西安'),(25,'陈小明','1','陕西西安'),(26,'王五',NULL,NULL);
/*Data for the table `orders` */
insert into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL);
/*Data for the table `orderdetail` */
insert into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3);
user和orders:一对多关系
user->orders:一个用户可以创建多个订单,一对多
orders->user:一个订单只能由一个用户创建,一对一
orders和orderdetail:(一对多关系)
orders->orderdetail:一个订单可以包括多个订单明细 一对多
orderdetail->orders:一个订单明细只能包括在一个订单中 一对一
select * from orders o,user u where o.user_id = u.id and u.number=?
分析:通过分析,orders表示主表,user表是辅助表
在Order的pojo类上添加上User属性
public class Orders213 {
private Integer id;
private Integer userId;
private String number;
private Date createTime;
private String note;
//新增属性
private User213 user;
//省略了getter和setter方法
}
Orders213 selectOrdersByNumber(String number);
<select id="selectOrdersByNumber" parameterType="string" resultType="com.tulun.Mybatis.pojo.Orders213">
select o.*,u.id "user.id",u.username "user.username",u.sex "user.sex",u.address "user.address" from orders o,user u where o.user_id = u.id and o.number= #{number}
</select>
<resultMap id="orderUserMap" type="com.tulun.Mybatis.pojo.Orders213">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="number" column="number"/>
<result property="createTime" column="createtime"/>
<result property="note" column="note"/>
<!--user属性配置-->
<result property="user.id" column="u_id"/>
<result property="user.username" column="u_username"/>
<result property="user.sex" column="u_sex"/>
<result property="user.address" column="u_address"/>
</resultMap>
<select id="selectOrdersByNumber" parameterType="string" resultMap="orderUserMap">
select o.*,u.id u_id,u.username u_username,u.sex u_sex,u.address u_address from orders o,user u where o.user_id = u.id and o.number= #{number}
</select>
<resultMap id="orderUserMap1" type="com.tulun.Mybatis.pojo.Orders213">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="number" column="number"/>
<result property="createTime" column="createtime"/>
<result property="note" column="note"/>
<association property="user" columnPrefix="u_" javaType="com.tulun.Mybatis.pojo.User213">
<result property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</association>
</resultMap>
优化:
extends继承自主类,resultMap使用一个已经存在的map类
<resultMap id="orderUserMap2" extends="orderMap" type="com.tulun.Mybatis.pojo.Orders213">
<association property="user" columnPrefix="u_" resultMap="com.tulun.Mybatis.mapper.User213Mapper.userMap"/>
</resultMap>
SqlSession sqlSession = sqlSessionFactory.openSession();
/**
*在通过sqlSession.getMapper可以获取一个代理对象
* 对于StudentMapper并没有显性的实现该接口的实现类,
* 该对象是在运行时动态产生的,该对象就是动态代理对象
*
* JDK动态代理
* 1、首先存在接口(StudentMapper.java)
* 2、必须存在该接口的实现类(sqlSession.selectOne)
* 3、实现invocationHandler辅助类
* 4、通过Proxy类来产生代理对象
*/
//单个对象原始调用方式
// Student student = sqlSession.selectOne("com.tulun.Mybatis.mapper.StudentMapper.selectStudentById", 4);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectStudentById(4);
System.out.println(student);
sqlSession.getMapper(StudentMapper.class);
Configuration configuration = new Configuration();
PooledDataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
DataSource dataSource = dataSourceFactory.getDataSource();
JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();
//设置环境
Environment environment = new Environment("test", jdbcTransactionFactory, dataSource);
configuration.setEnvironment(environment);
//设置mapper接口
configuration.addMapper(StudentMapper.class);
new SqlSessionFactoryBuilder().build(configuration);
configuration.addMapper(StudentMapper.class);
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {//只允许添加接口
if (hasMapper(type)) {//不允许重复添加
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public class MapperProxyFactory<T> {//映射器代理工厂
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//使用的是JDK自带的动态代理对象生成代理类对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
//实现了InvocationHandler接口
@Override
//代理之后,所有的mapper的方法调用,都会调用这个invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args); //该位置就调用了原始的调用方法:SQLSession.selectOne
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行CURD操作
return mapperMethod.execute(sqlSession, args);
}
}
//同一个SQLSession连续进行查询操作
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//第一次查询操作
System.out.println("第一次查询开始......");
Student student = studentMapper.selectStudentById(4);
System.out.println("第一次查询结束......");
System.out.println("第二次查询开始......");
Student student1 = studentMapper.selectStudentById(4);
System.out.println("第二次查询结束......");
//同一个SQLSession连续进行查询操作
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//第一次查询操作
System.out.println("第一次查询开始......");
Student student = studentMapper.selectStudentById(4);
System.out.println("第一次查询结束......");
//对数据进行变更操作
System.out.println("变更操作开始....");
studentMapper.updateNameById(4,"test");
sqlSession.commit();
System.out.println("变更操作结束....");
//第二次查询
System.out.println("第二次查询开始......");
Student student1 = studentMapper.selectStudentById(4);
System.out.println("第二次查询结束......");
SqlSession sqlSession = sqlSessionFactory.openSession();
//分别数据不同的SQLSession是否会命中缓存
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//第一次查询操作
System.out.println("第一次查询开始......");
Student student = studentMapper.selectStudentById(4);
System.out.println("第一次查询结束......");
SqlSession sqlSession1 = sqlSessionFactory.openSession();
StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);
//第二次查询
System.out.println("第二次查询开始......");
Student student1 = studentMapper1.selectStudentById(4);
System.out.println("第二次查询结束......");
二级缓存和一级缓存的区别:
二级缓存范围更大,多个SQLSession可以共享一个mapper级别的二级缓存,数据类型依然是HashMap来存储二级缓存内容,mapper是按照namespace划分,如果namespace相同则使用同一个二级缓存,
一级缓存范围会更小,是一个SQLSession级别
<settings>
<!--cacheEnabled:开启mybatis的二级缓存-->
<setting name="cacheEnabled" value=" true"/>
</settings>
2. 将映射的pojo类实现序列化
public class Student implements Serializable
3. 在mapper配置文件中使用cache标签
<!--
cache和二级缓存相关标签
eviction属性:代表缓存的回收策略,mybatis提供了回收策略如下:
LRU:最近最少使用,对于最长时间不用的对象进行回收
FIFO:先进先出,按照帝乡进入缓存的顺序来进行回收
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用,基于垃圾回收器状态和弱引用规则的对象
flushInterval属性:刷新间隔时间,单位是毫秒
size属性:引用数目,代表缓存可以存储最多多少个对象
readOnly属性:只读,意味着缓存数据只能读取不能修改
-->
<cache eviction="FIFO" flushInterval="1000" size="1024" readOnly="false"/>
//二级缓存的测试
@Test
public void testCache2() {
//不同的SQLSession会话进行相同的SQL查询操作
//SQLSession1实例
SqlSession sqlSession = sessionFactory.openSession();
Student23Mapper student23Mapper = sqlSession.getMapper(Student23Mapper.class);
//SQLSession2实例
SqlSession sqlSession1 = sessionFactory.openSession();
Student23Mapper student23Mapper1 = sqlSession1.getMapper(Student23Mapper.class);
//sqlsession实例3
SqlSession sqlSession2 = sessionFactory.openSession();
Student23Mapper student23Mapper2 = sqlSession2.getMapper(Student23Mapper.class);
//第一次查询id为1的用户
Student23 student23 = student23Mapper.selectStudentByUid(1L);
System.out.println(student23);
//这里执行关闭操作,将SQLSession中的数据写入到二级缓存区域
sqlSession.close();
//第二次查询id为1的用户
Student23 student24 = student23Mapper1.selectStudentByUid(1L);
System.out.println(student24);
sqlSession1.close();
}