一、MyBatis初次使用
2.1 环境搭建步骤
MyBatis 的 API : https://mybatis.org/mybatis-3/zh/getting-started.html
1.引入依赖包
2.准备核心配置件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://123.57.206.19:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root
password=123456
mybatis.xml
在resources下定义MyBatis的配置文件,无固定名,但大部分人使用 resources/mybatis.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="db.properties"></properties>
<!-- <settings>-->
<!-- <setting name="logImpl" value="LOG4J"/>-->
<!-- </settings>-->
<typeAliases>
<!--给单个类起别名。 type:类型 alias:别名-->
<typeAlias type="com.test.pojo.Student" alias="student"></typeAlias>
<!--给指定包下所有类起别名。 别名=类名(不区分大小写)-->
<package name="com.test.pojo"/>
</typeAliases>
<!-- 配置mybaits中数据库连接环境-->
<environments default="mysql">
<environment id="mysql">
<!--配置myabtis中事务 和 JDBC 保持一致-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接数据库的4个元素, 底层采用的是数据库连接池的方式-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--扫描mapper文件-->
<mappers>
<mapper resource="mapper/student.xml"></mapper>
</mappers>
</configuration>
3.书写mapper文件
resources/**.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">
<!--namespace: 代表xml的名称,类似java包名-->
<mapper namespace="com.beiyou.dao.StudentDao">
<!-- 查询所有学生 List<Student> selectAll()-->
<!--
select: 代表进行查询操作。
id: 之前的方法名称,具有唯一性。
resultType: 返回值类型。
如果返回的是对象,直接书写对象类型的的完整名。
如果是集合,书写的是集合的泛型
parameterType: 参数类型,可以省略。
-->
<select id="selectAll" resultType="com.beiyou.entity.Student" >
select * from student
</select>
</mapper>
非必需
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes>
<!--.xml 文件都会扫描到,包括子目录-->
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4.构建SqlSessionFactory。
从xml中创建SqlSessionFactory.
// 1. 解析扫码 mybatis.xml 文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
// 2. 获取sqlsession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 获得 sqlsession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 执行sql语句
List<Student> students = sqlSession.selectList("com.beiyou.dao.StudentDao.selectAll");
// 打印结果
System.out.println(students);
是否弥补了JDBC的不足?
二、MyBatis 配置细节
2.1 log4j的使用
-
加入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
-
配置文件 log4j.properties
#定义全局日志级别调试阶段推荐debug
log4j.rootLogger = error,stdout
#包级别日志
log4j.logger.test.a = debug
### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.SimpleLayout
### 输出日志到文件=/logs/log.log ###
log4j.appender.logfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File = /logs/log.log
log4j.appender.logfile.layout = org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
2.2 事务配置
transactionManager.type
JDBC : 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED : 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
2.3 连接池配置
dataSource.type
UNPOOLED : 这个数据源的实现会每次请求时打开和关闭连接.
POOLED : 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
JNDI : 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
2.4 映射文件的加载方式
1.resource: 使用相对于类路径的资源引用。
<mapper resource="AuthorMapper.xml"/>
2.url: 使用完全限定资源定位符(URL)
<mapper url="file:///D:/207/mybatis/src/main/resources/mapper/BlogMapper.xml"/>
3.class : 使用映射器接口实现类的完全限定类名
<mapper class="org.mybatis.builder.BlogMapper"/>
4.name : 将包内的映射器接口实现全部注册为映射器
<mappers>
<package name="com.beuyou.dao"/>
</mappers>
2.5 实体类别名处理
<typeAliases>
<!--给单个类起别名。 type:类型 alias:别名-->
<typeAlias type="com.beiyou.entity.Student" alias="student"></typeAlias>
<!--给指定包下所有类起别名。 别名=类名(不区分大小写)-->
<package name="com.beuyou.entity"/>
</typeAliases>
常见的 Java 类型内建的类型别名。它们都是不区分大小写的
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
2.6 外部属性配置文件存储数据库信息
-
配置db.properties数据库信息
driver=com.mysql.cj.jdbc.Driver
url=mysql://rm-bp169j3q9n43kxauzco.mysql.rds.aliyuncs.com:3306?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root123
password=Root_123
<properties resource="db.properties"></properties>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
三、Mapper文件配置
3.1 常用属性
3.2 SQL 定义标签
1. select
用于数据查询操作,例:
<select id="selectUserInfo" parameterType="int" resultType="map">
select * from user_info where id=#{keyId}
</select>
2. insert
用于数据保存操作,例:
<insert id="insertUserInfo" parameterType="map" useGeneratedKeys="true" keyProperty="keyId">
insert into user_info (
userName,
userSex
)values(
#{userName},
#{userSex}
)
</insert>
PS:keyProperty属性可返回此条插入数据的主键值
3. update
用于数据更新操作,例:
<update id="updateUserInfo" parameterType="map">
update user_info
set userName=#{userName}
where id=#{keyId}
</update>
4. delete
用于数据删除操作,例:
<delete id="selectUserInfo" parameterType="int">
delete from user_info
where id=#{keyId}
</delete>
5. resultMap
SQL返回与实体类映射关系信息,例
<resultMap id="userInfoMap" type="User">
<result property="user_name" column="userName"/>
<result property="user_sex" column="userSex"/>
</resultMap>
<select id="selectUserInfo" parameterType="int" resultType="userInfoMap">
select
userName,
userSex
from user_info
where id=#{keyId}
</select>
将数据表字段userName、userSex映射到实体类User的user_name、user_sex
6. sql
用于定义可重用的 SQL 代码片段,以便在多个SQL语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。例:
<!-- 定义 -->
<sql id="userColumns"> ${alias}.userName,${alias}.userSex</sql>
<!-- 运用 -->
<select id="selectUserInfo" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from user_info t1
left join user_info_copy t2
</select>
3.3、SQL动态标签
1. if
单个条件判断,用以实现条件筛选,例:
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
where 1=1
<if test="userSex !=null and userSex !='' ">
and userSex=#{userSex}
</if>
<if test="userName !=null and userName !='' ">
and userName like CONCAT('%',#{userName},'%')
</if>
</select>
2. foreach
用于更新或保存数据时的批量操作,例:
<!-- userList为List<HashMap<String,Object>>类型数据 -->
insert into user_info(
userName,
userSex
)values
<foreach item="item" index="index" collection="userList" separator="," >
(
#{item.userName},
#{item.userSex}
)
</foreach>
<!-- userList为List<String>类型数据 -->
insert into user_info(
userName
)values
<foreach item="item" index="index" collection="userList" separator="," >
(
#{userName}
)
</foreach>
update user_info
set userAge=#{userAge}
where id in
<foreach collection="keyIds" index="index" item="item" separator="," open="(" close=")">
#{item}
</foreach>
3. choose/when/otherwise
用以实现条件的多种判断,类似与if else,例:
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
where 1=1
<choose>
<when test="userFlag!=null and userFlag!='' and userFlag=='Y'">
and id<=100
</when>
<when test="userFlag!=null and userFlag!='' and userFlag=='N'">
and id <=200
</when>
<otherwise>
and id<=300
</otherwise>
</choose>
</select>
4. where
只会在子元素返回任何内容的情况下才插入 “WHERE” 子句,并且可以自动处理判断条件语句返回的第一个and或or,例:
不使用where标签时,若userSex为空,语法错误会报错:
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
where
<if test="userSex !=null and userSex !='' ">
userSex=#{userSex}
</if>
<if test="userName !=null and userName !='' ">
and userName like CONCAT('%',#{userName},'%')
</if>
</select>
修改为:
<select id="selectUserInfo" parameterType="map" resultType="map">
select * from user_info
<where>
<if test="userSex !=null and userSex !='' ">
userSex=#{userSex}
</if>
<if test="userName !=null and userName !='' ">
and userName like CONCAT('%',#{userName},'%')
</if>
</where>
</select>
自动转换为:select * from user_info where userName like ……
5. set
可以动态更新需要更新的列,忽略其它不更新的列,例:
<update id="updateUserInfo" parameterType="map">
update user_info
<set>
<if test="userName!= null and userName!=''">
userName=#{userName},
</if>
userSex=#{userSex}
</set>
where id=#{keyId}
</update>
四、基于MyBatis的CURD操作
使用单元测试验证
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
4.1 MyBatis查询的三种方式
-
返回单个对象 selectOne
-
返回对象List集合 selectList
-
返回对象Map集合 selectMap
<select id="selectOne" resultType="student">
select * from student where id=1
</select>
<select id="selectAll" resultType="student" >
select * from student
</select>
<select id="selectMap" resultType="map">
select * from student
</select>
4.2 MyBatis参数传递的三种方式
4.2.1 三种传参
-
传递的是基本类型+String ,使用 param1
-
传递类型是对象,接受使用对象的 属性名
-
传递的是map集合,接受时候使用map中的 key
<!-- 方法 Student selectOne(int id)-->
<!--param1:-->
<select id="selectOne" resultType="student" parameterType="int">
select * from student where id = #{id}
</select>
<!-- 方法 Student selectOne(StudentQuery query)-->
<!--#{} 里面放的是对象属性-->
<select id="selectOne2" resultType="student">
select * from student where id = #{id} and name = #{name}
</select>
<!-- 方法 Student selectOne(Map map)-->
<!--#{} 里面放的是map的key-->
<select id="selectOne3" resultType="student">
select * from student where id = #{a} and name = #{b}
</select>
// 【A】. 传递基本类型
Student student = sqlSession.selectOne("test.c.selectOne", 2);
// 打印
System.out.println(student);
// 【B】. 传递对象
StudentQuery query = new StudentQuery();
query.setId(2);
query.setName("周星星");
Student student = sqlSession.selectOne("test.c.selectOne2", query);
//打印
System.out.println(student);
//【C】. 传递Map集合
//id,name 一块封装到map集合
Map<String,Object> map = new HashMap<>();
map.put("a",2);
map.put("b","周星星");
Student student = sqlSession.selectOne("test.c.selectOne3", map);
//打印
System.out.println(student);
4.2.2 #和$区别:面试题
#:底层相当于占位符?
$:底层相当于字符串拼接
-
两者相比,占位符的方式更加方便,可以有效的防止SQL注入。
-
预编译
4.2.3 模糊查询
<!-- 模糊查询 -->
<select id="selectOne4" resultType="student">
Student student = sqlSession.selectOne("test.c.selectOne4", "%敏%");
select * from student where name like #{param1}
Student student = sqlSession.selectOne("test.c.selectOne4", "敏");
select * from student where name like concat('%',#{param1},'%')
</select>
4.2.4 Model对象字段名称与数据库不一致,使用resultMap指定
<select id="selectlike2" resultMap="usermap" >
select * from user where email like concat('%',#{param1},'%')
</select>
<resultMap id="usermap" type="User">
<!--
主键映射 使用id标签
propetry java中的类型名称
column 数据库中的字段名
-->
<id property="pwd" column="password"/>
</resultMap>
XML
4.2.5 include标签
1、首先定义一个sql标签,一定要定义唯一id。 <sql id="columns">
id, title ,brief
</sql>
2、然后通过id引用 <select id="selectOne" resultMap="productResultMap1" >
select
<include refid="columns"/>
from product where id = 8
</select>
XML
4.3 MyBatis完整DML全部操作
DML与DDL的含义:
1、DML(Data Manipulation Language)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言,简而言之就是实现了基本的“增删改查”操作。包括的关键字有:select、update、delete、insert、merge
2、DDL(Data Definition Language)数据定义语言-用于定义和管理 SQL 数据库中的所有对象的语言,对数据库中的某些对象(例如,database,table)进行管理。包括的关键字有:
create、alter、drop、truncate、comment、grant、revoke
4.3.1 CUD
【1】新增
<!-- 方法 int insert(Student student)-->
<insert id="insert">
insert into student (name,age) values (#{name},#{age})
</insert>
Student student = new Student();
student.setName("邓超");
student.setAge(38);
int rowNum = sqlSession.insert("com.beiyou.dao.StudentMapper.insert", student);
//MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事务
sqlSession.commit();
【2】修改
<!-- 传统方法 int update(Student student)-->
<update id="update">
update student set name = #{name},age = #{age} where id = #{id}
</update>
Student student = new Student();
student.setName("邓超111");
student.setAge(380);
student.setId(6);
int rowNum = sqlSession.update("com.beiyou.dao.StudentMapper.update", student);
//MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事务
sqlSession.commit();
【3】删除
<!-- 传统方法 int delete(int id)-->
<delete id="delete">
delete from student where id = #{param1}
</delete>
int rowNum = sqlSession.delete("test.d.delete", 6);
//MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事务
sqlSession.commit();
4.3.2 设置SqlSession提交
MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事
【1】SqlSession sqlSession = factory.openSession(true);
【2】sqlSession.commit();
4.4 扩展
4.4.1 接口编程
package com.beiyou.dao;
public interface UserDao {
List<User> selectAll();
}
<mapper namespace="com.beiyou.dao.UserDao">
<select id="selectAll" resultType="user" >
select * from 202_user
</select>
</mapper>
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.selectAll();
4.4.2通过表达式,实现多场景多条件组合查询
<select id="select" resultMap="productResultMap1">
select id, categoryId,title ,brief from 202_product
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="ids != null">
and id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
<if test="categoryId != null">
and categoryId= #{categoryId}
</if>
<if test="categoryIds != null">
and categoryId in
<foreach collection="categoryIds" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
<if test="name != null">
and title like concat('%',#{name},'%')
</if>
</where>
</select>
@Test
public void selectQuery() throws IOException {
ProductDao productDao = sqlSession.getMapper(ProductDao.class);
ProductQuery query = new ProductQuery();
//query.setId(40);
//query.setCategoryId(1);
//query.setName("梨38");
//query.setIds(new Integer[]{38,42,50,51,52});
query.setCategoryIds(new Integer[]{3});
List<Product> products = productDao.select(query);
System.out.println(products);
}
4.4.3 注解
<mapper class="com.beiyou.dao.UserDao"/>
public interface UserDao {
@Select("select * from 202_user limit 1")
User select(); //insert into order_item (productId,productName,productImg,price,qty,orderId) values (1,2,3),(2,3,4).....
@Insert("<script> " +
"insert into " +
" order_item (productId,productName,productImg,price,qty,orderId) " +
"values " +
"<foreach collection='items' item='item' separator=','> "+
"(#{item.productId},#{item.productName},#{item.productImg},#{item.price},#{item.qty},#{item.orderId})"+
"</foreach> </script>" )
int insertAll(List<OrderItemEntity> items);
@Select("<script> " +
"select * from order_item where 1 = 1 " +
"<if test='id != null'>" +
" and id = #{id} "+
"</if> "+
"<if test='orderId != null'>" +
" and orderId = #{orderId} "+
"</if> "+
"<if test='orderIds != null'>" +
" and orderId in "+
"<foreach collection='orderIds' open='(' close=')' item='item' separator=','> "+
"#{item}"+
"</foreach> "+
"</if> "+
"</script>" )@Results(id="studentMap",value={ @Result(column=“id”, property=“id”, jdbcType=JdbcType.INTEGER, id=true), @Result(column=“name”, property=“name”, jdbcType=JdbcType.VARCHAR), @Result(column=“class_id”, property=“classId”, jdbcType=JdbcType.INTEGER)})
List<OrderItemEntity> select(OrderItemQueryDto queryDto);
}
4.4.4 SelecKey标签使用
Mybatis之useGeneratedKeys和selectKey的基本用法与区别_mybatis selectkey usegeneratedkeys_poppyCL的博客-CSDN博客
一、useGeneratedKeys数据库本身具备主键自动增长的功能,才能使用useGeneratedKeysoracle不支持true<insert id="insert" useGeneratedKeys="true" keyProperty="idColName"> insert into tableName (colName) values (#{colVal,jdbc..._mybatis selectkey usegeneratedkeys
https://blog.csdn.net/poppyCL/article/details/103347385
<insert id="insert" parameterType="UserEntity">
insert user (email,password) values (#{email},#{pwd})
<selectKey keyProperty="id" resultType="integer" keyColumn="newId" order="AFTER">
SELECT LAST_INSERT_ID() as newId
</selectKey>
</insert>
<selectKey resultType="integer" keyColumn="newId" keyProperty="id" order="BEFORE">
SELECT (max(id)+1) as newId from 205_category
</selectKey>
注解版
@SelectKey(statement="SELECT last_insert_id", keyProperty="id", before=false, resultType=Long.class)
五 MyBatis 高级关系查询
-
一个会员只属于一个详情 ==> 会员对详情表是一对一关系
-
不管是一对一还是多对多,都要使用<resultMap> ,属性有id 和type
-
一对一中,<resultMap>内要用<association>来映射复杂对象,属性有 :
-
(property和javaType) ==> 嵌套结果
-
(property, column, select) ==> 嵌套查询
-
一对多中,<resultMap>内要用<collection>来映射复杂对象,属性有property和ofType
-
注意防范<resultMap>和<association>或<collection>中字段名冲突的问题!
5.1 一对一
<resultMap> <association>
<association> 元素,通常可以配置一下属性
- propery:指定映射到实体类对象属性,与表字段一一对应
- column:指定表中对应的字段
- javaType:指定映射到实体对象属性的类型
- select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询
- fetchType:指定在关联查询时是否启用延迟加载。FetchType属性有lazy和eager(实时)两个属性值,默认值为lazy
默认为lazy(默认关联映射延迟加载)
create table 202_user( id int unsigned auto_increment, tel varchar(50) not null, password varchar(32) not null, primary key(id));
CREATE TABLE `202_userinfo` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '唯一标识',
`name` varchar(100) NOT NULL COMMENT '姓名',
`sex` varchar(100) DEFAULT NULL COMMENT '性别',
PRIMARY KEY (`id`)
)
实体对象
@Data public class User { private Integer id; private String tel; private String password; private UserInfo userinfo;}public class UserInfo { private Integer id; private String name; private String sex; private Integer age;
}
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.beiyou.dao.UserDao2">
<resultMap id="usermap" type="com.beiyou.model.User">
<id property="id" column="id"/>
<result property="tel" column="tel"/>
<result column="password" property="password"/>
<association property="userInfo" javaType="com.beiyou.model.UserInfo">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
</association>
</resultMap>
<select id="one2one" resultMap="usermap" >
select *
from `202_user` u
left join `202_userinfo` ui
on u.id = ui.userId
</select>
<resultMap id="userMap" type="com.beiyou.model.User">
<id column="id" property="id"/>
<result column="tel" property="tel"/>
<result column="password" property="password"/>
<association property="userInfo" column="Id" fetchType="lazy" javaType="com.beiyou.model.UserInfo"
select="selectUserinfo">
<id property="id" column="id"/> bug 必须书写
<result property="name" column="name"/>
<result property="sex" column="sex"/>
</association>
</resultMap>
<select id="lazyone2one" resultMap="usermap2">
select * from 202_user
</select>
<select id="selectName" resultType="com.beiyou.model.UserInfo">
select * from 202_userinfo where userId = #{id}
</select>
</mapper>
UserDao.java
public interface UserDao {
User one2one(String name);
User lazyone2one(String name);
}
5.2 一对多
<resultMap> <collection>
CREATE TABLE `202_address` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '唯一id',
`userId` int NOT NULL COMMENT '用户编号',
`province` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '省',
`city` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '市',
`address` varchar(100) DEFAULT NULL COMMENT '详细地址',
PRIMARY KEY (`id`)
)
<resultMap id="userMap" type="com.beiyou.model.User">
<id column="id" property="id"/>
<result column="tel" property="tel"/>
<result column="password" property="password"/>
<association property="userInfo" column="Id" fetchType="lazy" javaType="com.beiyou.model.UserInfo"
select="selectUserinfo">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
</association>
<collection property="addresses" column="id" fetchType="lazy" javaType="java.util.ArrayList"
ofType="com.beiyou.model.Address" select="selectAddress" >
<id property="id" column="id"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="county" column="county"/>
<result property="address" column="address"/>
</collection>
</resultMap>
<select id="selectAddr" resultType="com.beiyou.model.Address">
select * from 202_address where userId = #{userId}
</select>
使用Mapper注解,实现一对一和一对多关系查询
@Results(id="userMap", value = {
@Result(column = "id", property = "id", id = true),
@Result(column = "tel", property = "tel"),
@Result(column = "password", property = "password"),
@Result(property = "userInfo", column = "id",
one = @One(select = "selectUserinfo",fetchType = FetchType.LAZY)), 可以不用写具体映射,但是用xml的时候,必须写
@Result(column = "id", property = "addresses" ,
many = @Many(select = "selectAddress",fetchType = FetchType.LAZY))
})
}
@Select("select * from 202_user u where u.id = #{id}")
List<User> layeOne2One(int id);
@Select("select * from 202_address where userId = #{id}")
List<Address> selectAddress(Integer id);
测试代码
@Test
public void test(){
UserMapper2 dao = sqlSession.getMapper(UserMapper2.class);
List<User> users = dao.queryUserAll();
}
Java
六 MyBatis缓存机制
mybatis.xml
<settings>
<setting name="cacheEnabled" value="true"/> //开启全局的二级缓存
</settings>
//清空缓存数据
@Options(flushCache = Options.FlushCachePolicy.TRUE)
@Select(" select * from 202_user where id= 46")
User one();
6.1 一级缓存
一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。
一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。
6.1.1何时清空一级缓存
如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。
6.1.2一级缓存无过期时间,只有生命周期
MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
6.2 二级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
同一个工厂生产的sqlsession,批次号相同.
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
6.2.1 二级缓存何时存入
在关闭sqlsession后(close或commit),才会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。
开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中
6.2.2二级缓存有过期时间
每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下.
6.2.3 执行流程
当 Mybatis 调用 Dao 层查询数据库时,先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。
SqlSessionFactory级别缓存,会话工厂级别
SqlSession s1 = sf.openSession();
SqlSession s2 = sf.openSession();
SqlSession s3 = sf.openSession();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
开发者必须自己配置二级缓存
二级缓存是人工开启的,需要在XxxxMapper.xml 文件中加入如下开启
方法一
<cache eviction="FIFO" flushInterval="60000" size="5120" readOnly="true" />
<select id="queryAll" resultType="book" useCache="false"> 默认使用缓存,填写false此操作不让缓存
select * from book
</select>
方法二
@CacheNamespace(eviction = FifoCache.class, flushInterval = 60000, size = 1024, readWrite = true)
public interface BookMapper {
@Select("select * from book")
@Options(useCache = true)
public List<Book> queryAll();
@Select("select * from book where id = #{id}")
public Book queryById(int id);
}
注意:使用缓存时,最好给实体类序列化。
Java
Student.java
@Data
public class Student implements Serializable {
private int id;
private String name;
private int age;
private double money;
private String info;
}
Plain Text
StudentMapper.java
@CacheNamespace
public interface StudentMapper {
@Select("select * from t_student")
@Options(useCache = true) //开启或关闭二级缓存
public List<Student> page();
@Select("select * from t_student where id = #{id}")
@Options(useCache = true)
public Student queryById(int id);
}
Java
@Test
public void t5() {
var session = sf.openSession();
var sm = session.getMapper(StudentMapper.class);
System.out.println(sm.page());
System.out.println(sm.page());
System.out.println(sm.page());
System.out.println(sm.page());
System.out.println("---------------------------");
System.out.println(sm.queryById(5));
session.commit();//将当前会话的查询,保存到二级缓存中,
System.out.println(sm.queryById(5));
System.out.println(sm.queryById(5));
System.out.println(sm.queryById(5));
System.out.println("----------------------");
var s2 = sf.openSession();
var sm2 = s2.getMapper(StudentMapper.class);
System.out.println(sm2.queryById(5));
}
Plain Text
六 常见问题
1.MySQL连接数据库时,添加语句:“allowMultiQueries=true”的作用:
-
可以在sql语句后携带分号,实现多语句执行。
-
可以执行批处理,同时发出多个SQL语句。