适合配套学习的教程:
Mybatis讲义.doc,连接:https://shimo.im/docx/m5kv9dp2XgcDGaqX/
MyBatis-3-User-Guide-Simplified-Chinese.pdf,连接:https://shimo.im/files/8Nk6MeN8gJf99nqL/
什么是框架:
它是一个半成品软件,将所有公共的,重复的代码解决掉,帮助程序快速高效的进行开发。它是可复用,可扩展的。
MyBatis 本是 apache 的一个开源项目iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为MyBatis 。2013 年 11 月迁移到 Github,最新版本是 MyBatis 3.5.7 ,其发布时间是 2021 年 4月 7日。MyBatis完成数据访问层的优化,它专注于sql语句,简化了过去JDBC繁琐的访问机制。
添加框架的步骤:
新建库建表
#创建数据库ssm
CREATE DATABASE ssm DEFAULT CHARSET utf8;
#使用(打开)ssm数据库
use ssm;
#创建表student
CREATE TABLE `student` (
`id` int(11) AUTO_INCREMENT primary key ,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into student(name,email,age) values('张三','zhangsan@126.com',22);
insert into student(name,email,age) values('李四','lisi@126.com',21);
insert into student(name,email,age) values('王五','wangwu@163.com',22);
insert into student(name,email,age) values('赵六','zhaoliun@qq.com',24);
select * from student;
新建maven项目,选quickstart模板
修改目录,添加缺失的目录,修改目录属性。这里主要是resources文件;
修改pom.xml文件,添加MyBatis的依赖,添加mysql的依赖;
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.3version>
dependency>
修改pom.xml文件,添加资源文件指定
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
在idea中添加数据库的可视化,主要是方便数据管理
添加jdbc.properties属性文件(数据库的配置)
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
添加SqlMapConfig.xml文件,MyBatis的核心配置文件
Mybatis核心配置文件头:
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
configuration>
加载数据库连接配置信息:
resource:会自动到资源目录resources下去扫描指定的文件;
url:这里指定的是绝对地址;
<properties resource="jdbc.properties"/>
配置环境:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
注册mapper.xml文件
<mappers>
<mapper resource="StudentMapper.xml"/>
mappers>
创建实体类Student,用来封装数据
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
// getter setter 有参无参构造函数 toString...
}
添加完成学生表的增删改查的功能的StudentMapper.xml文件
mapper文件头:
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
mapper标签:
<mapper namespace="student">
mapper>
查询所有的学生信息:
<select id="getAll" resultType="com.xuan.pojo.Student">
select id, name, email, age from student;
select>
按主键id查询学生信息
<select id="getById" parameterType="int" resultType="com.xuan.pojo.Student">
select id, name, email, age from student where id = #{id}
select>
按学生姓名进行查询
<select id="getByName" parameterType="string" resultType="com.xuan.pojo.Student">
select id, name, email, age from student where name like #{name}
select>
添加学生信息
Mybatis中,增删改在xml文件中是没有执行的返回结果(即,可以不写其返回值),可以通过SqlSession对象的相关方法获取执行结果。
<insert id="insert" parameterType="com.xuan.pojo.Student">
insert into student(name, email, age) values (#{name}, #{email}, #{age})
insert>
删除学生
<delete id="delete" parameterType="int">
delete from student where id = #{id}
delete>
删除学生
<update id="update" parameterType="com.xuan.pojo.Student">
update student set name = #{name}, email = #{email}, age = #{age} where id = #{id}
update>
创建测试类,进行功能测试
public class MyTest {
// 优化测试
SqlSession sqlSession;
/*
@Before注解:在所有被@Test注解标注的方法执行之前执行被@Before注解标注的方法里面的代码,注解@After则相反。
该方法实现:读取Mybatis核心配置文件、创建SqlSessionFactory、创建SqlSession对象
*/
@Before
public void getSqlSession() throws IOException {
// 使用文件流读取核心配置文件:SqlMapConfig.xml
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
// 取出sqlSession对象
sqlSession = factory.openSession();
}
/**
* 该方法用来关闭SqlSession对象。
* 每次执行被@Test注解标注的方法在执行完毕后都会执行该方法。
*/
@After
public void closeSqlSession(){
// 关闭sqlSession,实际上是将该链接放回到连接池
sqlSession.close();
}
@Test
public void testAll() throws IOException {
// 完成查询操作
List<Student> studentList = sqlSession.selectList("student.getAll");
studentList.forEach(System.out::println);
}
@Test
public void testSpecificStudent() throws IOException {
Student student = sqlSession.selectOne("student.getById", 1);
System.out.println(student);
}
@Test
public void testSpecificName() throws IOException {
List<Student> studentList = sqlSession.selectList("student.getByName", "%三%");
studentList.forEach(System.out::println);
}
@Test
public void testInsertAndDelAndUpdate() throws IOException {
// 添加
//sqlSession.insert("student.insert", new Student(1111, "hh", "hh@hhh.com", 20));
// 修改
// sqlSession.update("student.update", new Student(5, "babql", "babq@hhh.com", 222));
// 删除
sqlSession.delete("student.delete", 5);
// 切记这里要手动提交事务
sqlSession.commit();
}
}
Resources类
就是解析SqlMapConfig.xml文件,创建出相应的对象,代码如下:
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory接口
使用ctrl+h快捷键查看本接口的子接口及实现类:
这里可以看到,DefaultSqlSessionFactory是SqlSessionFactory接口的实现类,通过new SqlSessionFactoryBuilder().build(in)这种方式创建SqlSession对象时,底层使用的就是DefaultSqlSessionFactory实现类。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
SqlSession接口
DefaultSqlSession是SqlSession主要的实现实现类。SqlSession中的很多方法都是在DefaultSqlSession中进行实现的。
为实体类注册别名:
<typeAliases>
<package name="com.xuan.pojo"/>
typeAliases>
设置日志输出:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
在三层架构中,业务逻辑层要通过接口访问数据层的功能,但是这里有个问题,就是业务逻辑层不能直接方法DAO层xml文件中的方法(功能),所以这里使用到了动态代理来解决这也问题。
12.优化mapper.xml文件注册;
Users数据库表:
use ssm;
-- ----------------------------
-- Table structure for `users`
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(2) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `users` VALUES (1, '王五', '2000-09-10', '2', '安徽');
INSERT INTO `users` VALUES (2, '张三', '2001-07-12', '1', '北京市');
INSERT INTO `users` VALUES (3, '张小明', '1999-02-22', '1', '河南');
INSERT INTO `users` VALUES (4, '陈小亮', '2002-11-19', '1', '辽宁');
INSERT INTO `users` VALUES (5, '张三丰', '2001-03-10', '1', '上海市');
INSERT INTO `users` VALUES (6, '陈小明', '2002-01-19', '1', '重庆市');
INSERT INTO `users` VALUES (7, '王五四', '2001-05-13', '2', '天津市');
select * from users;
注册mapper.xml文件
<mappers>
<mapper url="/">mapper>
<mapper resource="StudentMapper.xml">mapper>
<mapper class="com.bjpowernode.mapper.UsersMapper">mapper>
<package name="com.bjpowernode.mapper">package>
mappers>
测试类
public class MyTest {
SqlSession sqlSession;
// Mybatis动态代理出来的对象
UserMapper userMapper;
// 格式化时间对象
SimpleDateFormat spf;
@Before
public void getSqlSession() throws IOException {
// 加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象
sqlSession = factory.openSession();
// 取出动态代理的对象,完成接口中方法的调用,实则是调用xml文件中相对应标签的功能
userMapper = sqlSession.getMapper(UserMapper.class);
// 格式化时间对象,年月日
spf = new SimpleDateFormat("yyyy-MM-dd");
}
}
#{}占位符:
传参大部分使用#{}传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问 ,可以有效的防止sql注入问题。#{}里如何写,这里主要是看parameterType参数的类型:
如果parameterType的类型是Java基本数据类型(或其封装类)与String,则#{}里面可以随便写;
<select id="getUserByUsername" resultType="user" parameterType="string">
select id, username, birthday, sex, address from users where username like #{username}
select>
如果parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写;
<update id="update" parameterType="user">
update users set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address}
where id = #{id}
update>
${}: 通常用于字符串拼接或字符串替换,具体如下:
字符串拼接,一般用于模糊查询中.建议少用,因为有sql注入的风险。 也分两种情况,同样的看parameterType的类型:
```xml
```
如果parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称(现在已经少用);
优化后的模糊查询(以后都要使用这种方式);
<select id="getUserByUsername" resultType="user" parameterType="string">
select id, username, birthday, sex, address from users where username like concat('%', #{username}, '%')
select>
字符串替换(使用的还有点多)
需求:要求模糊查询地址或用户名;
select * from users where username like '%小%';
select * from users where address like '%市%';
这里如果是按照上面的方式写sql,那么可能就要对应两个不同的方法,所以这里可以使用替换的方式使用一个方法进行实现。
/**
* 通过列名与列值获取对应的用户信息。这里@Param参数value可以与被修饰的参数名不一样,但是规范要求一样。
* @param columnName 对应表的列的名称,为了能够在xml文件中正确的取到该值,这两个参数必须使用@Param注解进行修饰。
* @param columnValue 对应表的列的值。
* @return 返回查询的用户的信息。
*/
List<User> getUserByUsernameOrAddr(
@Param("columnName")
String columnName,
@Param("columnValue")
String columnValue
);
<select id="getUserByUsernameOrAddr" resultType="user">
select id, username, birthday, sex, address
from users
where ${columnName} like concat('%', #{columnValue}, '%')
select>
业务描述:当新增一个用户之后,给用户id积分自动加500。
这里涉及一个问题,用户表中的uid字段是自增,当添加用户时,用户的id是自动生成的,那么这里在新增一个用户后,如何同时获取到自动生成的uid给用户在积分表中添加积分值?这里可以使用mysql中的一个函数last_insert_id(),该函数会自动返回最新插入到表中的数据行对应的id。下面来解决这个问题。
在插入语句结束后, 返回自增的主键值到入参的users对象的id属性中。
/**
* 新增用户,添加之后返回用户的id。
* @param user 添加的对象;
* @return 返回影响的行数;
*/
int insert(User user);
<!--
int insert(User user);
keyProperty:使用user对象的id属性来接收返回的主键值;
resultType:返回的主键值的类型;
order:在插入语句执行之前还是之后返回主键的值;
-->
<insert id="insert" parameterType="user">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into users(username, birthday, sex, address) values (#{username}, #{birthday}, #{sex}, #{address})
</insert>
@Test
public void testInsert() throws ParseException {
User user = new User(11, "fjaidfdasfad", spf.parse("1999-02-03"), "2", "广东深圳");
userMapper.insert(user);
sqlSession.commit();
System.out.println(user);
}
这是一个全球唯一随机字符串,由36个十六进制数以及中划线组成。
// UUID
@Test
public void testUUID(){
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString().replace("-", ""));
}
可以定义代码片断,进行逻辑判断,循环处理(批量处理),使条件判断更为简单;
:用来定义代码片断,可以将所有的列名,或复杂的条件定义为代码片断,供使用时调用,实现sql的重复使用;
<sql id="allColumns">
id, username, birthday, sex, address
sql>
:用来引用定义的代码片断;
<select id="allUsers" resultType="user">
select <include refid="allColumns"/> from users
select>
:进行条件判断;
test条件判断的取值可以是实体类的成员变量,可以是map的key,可以是@Param注解的名称
:进行多条件拼接,在查询,删除,更新中使用;
/**
* 进行多条件查询。
* @param user 传入一个对象,判断对象是否为null,如果不为空,则该属性值即为查询条件之一。
* @return 查询的对象;
*/
List<User> moreConditionQuery(User user);
<select id="moreConditionQuery" resultType="user" parameterType="user">
select <include refid="allColumns"/>
from users
<where>
<if test="username != null and username != ''">
and username like concat('%', #{username}, '%')
if>
<if test="birthday != null">
and birthday = #{birthday}
if>
<if test="sex != null">
and sex = #{sex}
if>
<if test="address != null and address != ''">
and address like concat('%', #{address}, '%')
if>
where>
select>
// 多条件查询
@Test
public void testMoreConditionQuery(){
// 默认为查询所有的用户
User user = new User();
// 查询用户名中包含“小”的用户
user.setUsername("小");
// 并且查询所在地址中包含“市”的字
user.setAddress("市");
List<User> userList = userMapper.moreConditionQuery(user);
userList.forEach(System.out::println);
}
底层执行的sql语句分析,以上的sql使用了与标签进行了条件的判断。当查询的用户对象所有的属性全为空时,底层执行的sql为:
当用户属性有一个不为空时,这里以用户名为小作为查询条件。执行的sql为:
这里有个问题值得注意,就是我们在xml文件中,已经写了当用户名不为空时将用户名加入查询的条件:
<if test="username != null and username != ''">
and username like concat('%', #{username}, '%')
if>
可以看到用户名前面的and已经被丢弃了,所以,当以多个参数作为条件时,其中如果有一个参数不为空,则会为该sql自动加上where条件,并且第一个不为空的参数前面的and会被省略。
:有选择的进行更新处理,至少需要更新一列。 能够保证如果没有传值进来,则数据库中的数据保持不变;注意点:前面演示的更新操作中其实都存在问题,即当用户传入的user对象只有username属性不为空,这时如果进行更新操作,其他的字段都会被替换为null。
所以在进行更新操作前需要判断用户出入的属性是否为空,如果为空该字段就不进行更新。
/**
* 选择性的进行更新操作。
* @param user 更新的用户;
* @return 返回影响行数;
*/
int conditionUpdate(User user);
<update id="conditionUpdate" parameterType="user">
update users
<set>
<if test="username != null and username != ''">
username = #{username},
if>
<if test="birthday != null">
birthday = #{birthday},
if>
<if test="sex != null">
sex = #{sex},
if>
<if test="address != null and address != ''">
address = #{address},
if>
set>
where id = #{id}
update>
// 选择更新
@Test
public void testConditionUpdate(){
User user = new User();
user.setId(9);
user.setUsername("张宇");
userMapper.conditionUpdate(user);
sqlSession.commit();
}
底层执行的sql语句为:
这种方式必须至少更新一列,否则回报一下的错误:
< foreach>:用来进行循环遍历,完成循环条件查询,批量删除(常用),批量增加(偶尔用),批量更新(很少用);
查询实现:
<select id="queryMoreUserById" resultType="user">
select <include refid="allColumns"/>
from users
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
foreach>
select>
<delete id="batchDel">
delete from users
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
foreach>
delete>
<insert id="batchInsert">
insert into users(username, birthday, sex, address) VALUES
<foreach collection="list" separator="," item="user">
(#{user.username}, #{user.birthday}, #{user.sex}, #{user.address})
foreach>
insert>
如果入参是多个,可以通过指定参数位置进行传参。如果是实体包含不住的条件参数类型,那么就只能使用形参这种方式了。例如:查询指定日期范围内的用户信息。
/*
一个方法有多个参数,且这些参数无法通过一个对象进行封装。这里有两种解决方案(不推荐使用):
1. 使用@Param()注解;
2. 使用#{args0},#{args1}...
*/
// 查询指定日期出生的用户
List<User> queryByBirthday(Date beginDate, Date endDate);
<select id="queryByBirthday" resultType="user">
select <include refid="allColumns"/>
from users
where birthday between #{arg0} and #{arg1}
select>
@Test
public void testQueryByBirthday() throws ParseException {
Date beginDate = spf.parse("2002-01-01");
Date endDate = spf.parse("2002-12-31");
List<User> users = userMapper.queryByBirthday(beginDate, endDate);
users.forEach(System.out::println);
}
如果入参超过一个以上,使用map封装查询条件,更有语义,查询条件更明确。
// 使用Map对上面的方法进行优化(推荐使用,也是应该重点掌握的)
List<User> queryByBirthdayMap(Map<String, Date> map);
<select id="queryByBirthdayMap" resultType="user">
select <include refid="allColumns"/>
from users
where birthday between #{beginDate} and #{endDate}
select>
@Test
public void testQueryByBirthdayMap() throws ParseException {
Map<String, Date> map = new HashMap<>();
Date beginDate = spf.parse("2002-01-01");
Date endDate = spf.parse("2002-12-31");
map.put("beginDate", beginDate);
map.put("endDate", endDate);
List<User> users = userMapper.queryByBirthdayMap(map);
users.forEach(System.out::println);
}
如果返回的数据实体类无法包含,可以使用Map返回多张表中的若干数据.返回后这些数据之间没有任何的内在联系。就是Object类型,返回的Map的key就是查询查询sql语句的列名或别名。
<select id="queryReturnMap" resultType="map" parameterType="int">
select username, address
from users
where id = #{id}
select>
<select id="queryMulMap" resultType="map">
select username, address
from users
select>
sql代码:
USE ssm;
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`bookid` INT(11) NOT NULL AUTO_INCREMENT,
`bookname` VARCHAR(32) NOT NULL COMMENT '图书名称',
PRIMARY KEY (`bookid`)
) ENGINE=INNODB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8;
INSERT INTO `book` VALUES (1, 'java基础');
INSERT INTO `book` VALUES (2, 'sql基础');
SELECT * FROM book;
当实体类的属性与表中的字段名不一致的时候,这是无法直接对数据进行封装,通常有以下的解决方案:
public class Book {
private Integer id;
private String name;
// ... gettter and setter and constructor and toString()
}
<select id="allBook" resultType="book">
select bookid id, bookname name from book
select>
<resultMap id="bookMap" type="book">
<id property="id" column="bookid"/>
<result property="name" column="bookname"/>
resultMap>
<select id="allBook" resultMap="bookMap">
select bookid, bookname from book
select>
关联关系是有方向的。
sql代码:
USE ssm;
CREATE TABLE customer(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32),
age INT2
);
INSERT INTO customer VALUES(1,'张三',22);
INSERT INTO customer VALUES(2,'李四',23);
INSERT INTO customer VALUES(3,'王五',24);
CREATE TABLE orders(
id INT PRIMARY KEY AUTO_INCREMENT,
orderNumber VARCHAR(16),
orderPrice DOUBLE,
customer_id INT
);
INSERT INTO orders VALUES(11,20,22.22,1);
INSERT INTO orders VALUES(12,60,16.66,1);
INSERT INTO orders VALUES(13,90,19.99,2);
SELECT * FROM customer;
SELECT * FROM orders;
客户和订单就是典型的一对多关联关系,一个客户名下可以有多个订单。客户表是一方,订单表是多方,通常一个客户表中持有订单的集合,使用一对多的关联关系,可以满足查询客户的同时查询该客户名下的所有订单。
<select id="allCustomer" resultMap="customerMap" parameterType="int">
select c.id cid, name, age, o.id, ordernumber, orderprice, customer_id
from customer c left join orders o on c.id = o.customer_id
where c.id = #{id};
select>
<resultMap id="customerMap" type="customer">
<id property="id" column="cid"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<collection property="ordersList" ofType="orders">
<id property="id" column="oid"/>
<result property="orderNumber" column="orderNumber"/>
<result property="orderPrice" column="orderPrice"/>
collection>
resultMap>
订单和客户就是多对一的关联,站在订单的方向查询订单的同时将客户信息查出。订单是多方,持有一方的对象,客户是一方。
package com.xuan.mapper;
import com.xuan.pojo.Orders;
public interface OrdersMapper {
// 查询指定的订单包括下单的用户,注:为了简单起见,这里的用户只有一个,即:订单与用户为多对一的关系。
Orders getOrderById(Integer id);
}
<mapper namespace="com.xuan.mapper.OrdersMapper">
<select id="getOrderById" parameterType="int" resultMap="orderMap">
select o.id oid, ordernumber, orderprice, customer_id, c.id cid, name, age
from orders o inner join customer c on c.id = o.customer_id
where o.id = #{id};
select>
<resultMap id="orderMap" type="orders">
<id property="id" column="oid"/>
<result property="orderNumber" column="orderNumber"/>
<result property="orderPrice" column="orderPrice"/>
<association property="customer" javaType="customer">
<id property="id" column="cid"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
association>
resultMap>
mapper>
一个班级只有一个授课老师,一个老师也只为一个班级授课。
总结:无论是什么关联关系,如果某方持有另一方的集合,则使用标签完成映射,如果某方持有另一方的对象,则使用标签完成映射。
多个操作同时完成,或同时失败称为事务处理。事务有四个特性(ACID):一致性,持久性,原子性,隔离性。
下订单的业务:
在MyBatis框架中设置事务:
// 程序员自己控制处理的提交和回滚
<transactionManager type="JDBC"></transactionManager>
可以在创建SqlSession对象的时候设置事务:
sqlSession = factory.openSession(); =>默认是手工提交事务,设置为false也是手工提交事务,如果设置为true,则为自动提交。
sqlSession = factory.openSession(true); =>设置为自动提交,在增删改后不需要commit();
MyBatis框架提供两级缓存,一级缓存和二级缓存。默认开启一级缓存;缓存就是为了提交查询的效率。
使用缓存后,查询的流程:
查询时先到缓存里查,如果没有则查询数据库,在数据库查到数据后先将该数据放入缓存,再从缓存当中给返回客户端。下次再查询的时候直接从缓存返回,不再访问数据库。如果数据库中发生commit()操作,则缓存会被全部清空。
一级缓存使用的是SqlSession的作用域,同一个sqlSession共享一级缓存的数据。
二级缓存使用的是mapper的作用域,不同的sqlSession只要访问的同一个mapper.xml文件,则共享二级缓存作用域。
// 测试缓存
@Test
public void testCache(){
User u1 = userMapper.getUserById(1);
System.out.println("user1: " + u1);
System.out.println("=======================");
User u2 = userMapper.getUserById(1);
System.out.println("user2: " + u2);
// 直接比较内存地址是否相同,如果相同则是同一份数据,否则不是。
System.out.println("user1 == user2 : " + (u1 == u2));
}
public void testCache() throws ParseException {
User u1 = userMapper.getUserById(1);
System.out.println("user1: " + u1);
System.out.println("=======================");
// 执行更新操作
userMapper.update(
new User(1, "王二五", spf.parse("2020-01-01"), "2", "花溪区大学城"));
// 执行增删改之后,一定要提交事务
sqlSession.commit();
User u2 = userMapper.getUserById(1);
System.out.println("user2: " + u2);
System.out.println("user1 == user2 : " + (u1 == u2));
}
ORM(Object Relational Mapping):对象关系映射,MyBatis框架是ORM非常优秀的框架,java语言中以对象的方式操作数据,存到数据库中是以表的方式进行存储,对象中的成员变量与表中的列之间的数据互换称为映射,整个这套操作就是ORM。
持久化的操作:将对象保存到关系型数据库中 ,将关系型数据库中的数据读取出来以对象的形式封装。
MyBatis是持久化层优秀的框架。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.allUser(); // 待分析