文章目录
一、MyBytis框架介绍
1、简介
- 基于sql的对象关系映射框架,简称ORM,持久层框架
- 对象关系映射:实体类映射为表,属性映射为表的字段,实体类的对象映射为表的记录;实体类之间的关联关系映射为表的关联关系。我们操作实体类对象,就相当于操作表的记录。
- 对象关系映射框架还有:Hibernate、MyBatis/MyBatisPluse、SpringData、Ejb(CMP)
2、使用MyBatis相对于传统JDBC的优点
- 传统的JDBC开发硬编码,耦合度高。MyBatis使用简单的 XML 或注解来配置,实现了解耦,方便统一管理sql语句;
- 提供对象关系映射功能,自动将sql执行结果映射为Java的类,不用我们自己封装
- MyBatis使用数据库连接池,也不用在众多程序手动创建连接
- MyBatis底层使用JDBC,与各个数据库兼容性高
3、MyBatis的核心组件
- mybatis的配置文件mybatis-config.xml文件
- Sql映射文件
- SqlSessionFactory:加载mybatis-config核心配置文件,连接数据库
- SqlSession:用于解析sql映射文件,查找sql映射文件绑定的接口类,调用请求的方法,执行解析的sql语句并根据映射文件中指定的类型返回给调用者。
- 参数类型组件ParamateType(@Parm注解描述参数)
- 返回类型组件ResultTyp、ResultMap
在映射文件中用于描述接口中方法的参数类型和返回类
<select id=”接口的方法名称” ParamateType=”参数的类型” resultType=”返回值的类型”>
sql语句
</select>
4、多种技术之间的区别
- MyBatis相比于原始JDBC操作:省去了创建连接对象、sql执行对象、结果集对象、封装结果集为实体类的操作。也通过配置实现了解耦,方便统一管理sql语句
- Spring整合MyBatis相比于原始MyBatis:实现了Spring控制反转的思想,将SqlSessionFactory对象通过容器注入程序
- SpringBoot整合的MyBstis相比于Spring整合的:省去了编写配置文件或者配置类的步骤,约定优于配置,大部分都采用默认的
二、原始MyBatis开发
1、导入依赖
导入MyBatis和MySQL驱动
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
2、MyBatis的配置文件内配数据源与sql映射文件
在MyBatis的官网就有模板,<mapper/>
标签内写sql映射文件的路径
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/dataBaseName"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载sql的映射文件 -->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
3、编写DAO类
public interface StudentDao {
public int save(Student st);
public int update(Student st);
public int delById(Integer id);
public Student findById(Integer id);
public int getSumRow();
public List<Student> findPageAll(@Param("nowPage") Integer nowPage, @Param("pageSize") Integer pageSize);
}
4、编写sql的映射文件
模板也在MyBatis官网
4.1 标签的属性
namespace
- mapper标签的属性,绑定dao中的接口,全类名
- 做名称空间,
名称空间.sql标识
区分不同mapper中相同名字的sql语句标识,比如不同的表,但是做查询的sql的id都叫queryById
id
- sql语句的标识
- 用于绑定接口中的方法(与方法同名)
parameterType
用于指定方法的参数类型
resultType
- 查询标签的属性,表示查出来的个体映射为哪个Java类。只有查询的标签有这个属性
- 返回值为集合,resultType是集合内的元素类型而不是集合类型。
4.2 sql语句中获取dao方法传递的参数
- 参数只有一个,在标签中用parameterType指定;参数大于一个,在接口的参数前面加注解@Param(“参数名”)指定。
- 参数是简单数据类型
#{参数名}
;参数是实体对象就是#{对象属性}
int save(Product product);
int delById(Integer pid);
<insert id="save" parameterType="com.jz.entity.Product">
insert into product(pname,price,num,pdate,cid,photo,tids,note)
values (#{pname},#{price},#{num},#{pdate},#{cid},#{photo},#{tids},#{note})
</insert>
<delete id="delById" parameterType="java.lang.Integer">
delete from product where pid=#{pid}
</delete>
4.3 #{}与${}
的区别
#{}:进行预编译处理,把参数值设置为 SQL 的参数占位符(例如?),然后在执行SQL语句时用参数值填充占位符。可以避免sql注入。可以处理参数的类型转换。
${}:进行文本替换,将 ${} 中的内容直接替换为参数值或者其他字符串,不会进行预编译,也不会类型转换,适用于需要动态拼接sql的情况(比如分页查询的nowPage和pageSize)。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace属性:绑定接口,作为命名空间 -->
<mapper namespace="com.jz.dao.StudentDao">
<!--
id绑定接口中的方法,也是sql的标识;
parameterType指定方法的参数的类型;
#{}取出对象的属性值,转为字符串拼接进sql
-->
<insert id="save" parameterType="com.jz.entity.Student">
insert into student(sname,gender,address,birthday,classname,note)
values(#{sname},#{gender},#{address},#{birthday},#{classname},#{note})
</insert>
<update id="update" parameterType="com.jz.entity.Student">
update student set sname=#{sname},gender=#{gender},
address=#{address},
birthday=#{birthday},
classname=#{classname},
note=#{note}
where stuid=#{stuid}
</update>
<delete id="delById" parameterType="java.lang.Integer">
delete from student where stuid=#{id}
</delete>
<select id="findById" parameterType="java.lang.Integer" resultType="com.jz.entity.Student">
select * from student where stuid=#{id}
</select>
<select id="findAll" resultType="com.jz.entity.Student">
select * from student order by stuid
</select>
<select id="findPageAll" resultType="com.jz.entity.Student">
select * from student order by stuid limit ${(nowPage-1)*pageSize},${pageSize}
</select>
</mapper>
4.4 使用
public class StudentTest {
public static void save() throws IOException, ParseException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setSname("刘禅");
student.setGender("男");
student.setAddress("成都");
student.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("2024-07-26"));
student.setClassname("java速成班");
student.setNote("是个乖孩子");
int result = mapper.save(student);
if (result>0){
System.out.println("保存成功");
sqlSession.commit();//提交事务
} else {
System.out.println("保存失败");
sqlSession.rollback();//回滚事务
}
sqlSession.close();
}
public static void findAll() throws IOException, ParseException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> all = mapper.findAll();
all.forEach(student -> {
System.out.println(student);
});
sqlSession.close();
}
public static void showPageAll() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> all = mapper.findPageAll(2, 5);
all.forEach(student -> {
System.out.println(student);
});
sqlSession.close();
}
public static void main(String[] args) throws IOException, ParseException {
//save();
//findAll();
showPageAll();
}
}
原始不使用mapper代理,就是直接用sqlSession对象调用insert等方法,通过传递参数命名空间.sql标识
执行sql语句
5、Mapper代理开发的好处
- 首先它不依赖于字符串字面值,会更安全一点;
- 其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL 语句。
6、实体类属性与表字段名不一样
比如表的字段是user_id,User实体类属性是uid
6.1 方式一
用<resultMap>
标签来绑定字段名与属性名的映射,同时<select>
标签内要指定resultMap
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jz.dao.UserDao">
<select id="findAll" resultMap="findAllResultMap">
select * from users;
</select>
<!-- 关联实体类属性与表字段的映射关系 -->
<resultMap id="findAllResultMap" type="com.jz.entity.Users">
<result property="uid" column="user_id" />
<result property="uname" column="user_name" />
<result property="passwd" column="user_passwd" />
</resultMap>
</mapper>
6.2 方式二
直接给字段起别名,别名就对应实体类的属性
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jz.dao.UserDao">
<select id="findAll" resultType="com.jz.entity.Users">
select user_id as uid, user_name as uname, user_passwd as passwd from users;
</select>
</mapper>
7、表的关联映射
将从表的数据映射(封装))到主表的实体类中
之前的是将本表的字段数据自动封装到自己的实体类属性
当表和表之间有主外键关联时,使用resultMap实现关联查询。
7.1 怎么判断主表和从表?
当A表的外键关联B表的主键,那么B表就是主表,A表就是从表
7.2 映射步骤
- 为主表的实体类增加一个Set集合属性存储从表数据
比如Product商品表,有外键cid,关联Catalog种类表的主键cid
在Catalog的实体类新增一个Set<Product>
集合属性存储商品对象
错误做法
Catalog的DAO:public List<Catalog> findAll();
Catalog的sql映射文件
<select id="findAll" resultType="com.entity.Catalog">
select * from catalog order by cid
</select>
查出来:主表是没有products字段的,不会成功赋值,product属性为null
CatalogDao catalogDao=sqlSession.getMapper(CatalogDao.class);
List<Catalog> catalogList=catalogDao.findAll();
catalogList.forEach(c->{
System.out.println(c);
});
正确做法:通过resultMap关联对应,将另一个sql的返回结果赋值给这个对象
7.1.2 使用resultMap标签,实现一对多关联映射
延时加载:只有当你用的时候(findAll或findById的时候查从表的属性)才会给他赋值,不用的时候就是null。节省系统资源
<?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.dao.CatalogDao">
<resultMap id="catalogResultMap" type="com.entity.Catalog">
<!--
collection:一对多的关联映射标签
property:主表实体类中存储子表的集合属性名
fetchType="":eager表示加载Catalog类的时候立即加载子表的实体类(默认),lazy延时加载
column="cid":表示关联子表的字段
ofType="com.entity.Product":关联的子表实体类
select="findProductsByCid":查询子表数据的查询方法,将该查询结果的返回值赋值给products属性
-->
<collection property="products" fetchType="eager"
column="cid"
ofType="com.entity.Product"
select="findProductsByCid"
/>
</resultMap>
<select id="findById" resultMap="catalogResultMap" parameterType="java.lang.Integer">
select * from catalog where cid=#{cid}
</select>
<select id="findAll" resultMap="catalogResultMap">
select * from catalog order by cid
</select>
<select id="findProductsByCid" parameterType="java.lang.Integer" resultType="com.entity.Product">
select * from product where cid=#{cid}
</select>
</mapper>
7.2 子表的关联查询
7.2.1 子表的实体类中定义主表的实体类对象作为属性
因为具体的物品对应的主表的记录只有一个,所以不用集合。就比如一部手机的内存大小是固定的,一个物品的种类肯定也是固定的。所以用类对象接受而不是集合。
7.2.2 使用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.dao.ProductDao">
<resultMap id="productResultMap" type="com.entity.Product">
<!-- association标签:多对一的关联映射
property="catalog":从表中对应的主表的对象属性
column="cid":外键关联字段
select="findCataLog":根据哪个方法获取主表对象
-->
<association property="catalog"
javaType="com.entity.Catalog"
column="cid"
select="findCataLog"/>
</resultMap>
<!-- 方法一:通过resultMap标签 -->
<select id="findAll" resultMap="productResultMap">
select * from product where 1=1 order by pid
</select>
<!-- 方法二:直接写多表联查的sql,只能用于子表联查 -->
<!--<select id="findAll" resultType="com.entity.Product">
select p.*,c.cname from product p,catalog c where p.cid=c.cid
</select>-->
<select id="findCataLog" parameterType="java.lang.Integer" resultType="com.entity.Catalog">
select * from catalog where cid=#{cid}
</select>
</mapper>
方法2需要在从表实体类中写上需要查的属性,如例中的cname
8、动态sql
- choose-when:满足条件的when都执行
多条件的模糊查询
<!-- select * from employee where 1=1 and ename like '张%' -->
<select id="queryChoose" resultType="com.company.bean.Employee">
select * from employee where 1=1
<choose>
<when test="eName != null">
and ename like '${eName}%'
</when>
<when test="eAddr != null">
and eAddr like '${eAddr}%'
</when>
<otherwise>
order by ename;
</otherwise>
</choose>
</select>
- if:
实现多条件更新
<!-- update employee SET eName = ?, eAddr = ? where eid = ? -->
<update id="update">
update employee
<set>
<if test="eName!=null">eName = #{eName},</if>
<if test="ePass!=null">ePass = #{ePass},</if>
<if test="eAddr!=null">eAddr = #{eAddr}</if>
</set>
where eid = #{eid}
</update>
- foreach循环执行
实现批量删除操作:集合参数传递
<!-- delete from employee where eid in ( ? , ? ) -->
<delete id="deleteByList">
delete from employee where eid in
<foreach item="eid" collection="list" open="(" separator="," close=")">
#{eid}
</foreach>
</delete>
Java实现
List<Integer> list = new ArrayList<>();
list.add(10020);
list.add(10021);
int i = employeeDao.deleteByList(list);
9、MyBatis的cache缓存
9.1 缓存的概念意义
缓存:缓存是计算机中的一块存储查询结果的区域,在第一次查询后将数据放进去,之后的查询直接从这个区域读取,不频繁访问数据库,加快查询效率。
缓存用于需要频繁访问数据库的单表查询
9.2 mybatis开启cache
在mapper标签内的最顶部加入<cache/>
标签开启
9.3 缓存分类
9.3.1 一级缓存(默认开启)
9.3.1.1 定义
一级缓存也称本地缓存,作用域是SqlSession对象,表示一次数据库会话
9.3.1.2 原理
- mybatis会在SqlSession对象中建立一个简单的缓存,这个缓存是Map集合,sql语句是主键,sql查出来的值是map的值
- 进行下一次查询的时候,会先判断是不是跟之前的查询完全相同,也就是判断sql语句(主键)是否相同,相同就直接从map集合中拿
9.3.1.3 生命周期
- 会话结束时,SqlSession对象都释放掉
- SqlSession对象调用clearCache方法清除缓存
- SqlSession对象调用close释放掉
- 执行增删改操作是会重建SqlSession的,缓存会清空,因为要更新缓存中的数据
9.3.2 二级缓存
MyBatis的二级缓存是application级别的,应用程序级别,需要通过第三方缓存库来实现
第三方缓存库有内存、系统、磁盘缓存库(EHCache用的比较多的)等等
9.3.2.1 使用二级缓存步骤
- 在mybatis的配置文件中开启二级缓存
- 对应的sql配置文件设置
<cache/>
- select标签的属性useCaChe设置为true
三、Spring整合MyBatis——纯注解
spring整合就是将数据源放在了容器中注入mybatis,并且注入代替了创建sqlsessionfactory等的对象
springboot整合连注入都省了,只需要在properties指定一下mybatis的配置文件
1、编写jdbc和mybatis的配置类
jdbc配置类前面有,这里只写mybatis配置类
@MapperScan("com.mapper")
public class MyBatisConfig {
//注入SqlSessionFactoryBean对象
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setDataSource(dataSource);
return ssfb;
}
}
1.1 注入SqlSessionFactoryBean对象
作用是生成SqlSessionFactory对象,SqlSessionFactory可以生成SqlSession对象操作数据库。
还要为其设置数据源
1.2 为接口创建动态代理对象
@MapperScan()
:给mybatis配置类上加该注解扫描代理接口所在的包,为扫描的包内的接口创建动态代理对象
2、将jdbc和mybatis配置类导入spring配置类
@Configuration
@ComponentScan("com")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
public class SpringConfig {
}
3、创建mapper接口
4、使用
四、Spring整合MyBatis——xml
1、导入spring-mybatis的依赖
2、编写mybatis的配置文件
在mappers标签下,mapper标签的resource属性指定sql映射文件的位置
<?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>
<mappers>
<mapper resource="com/jz/dao/ProductDao.xml" />
<mapper resource="com/jz/dao/TeZhengDao.xml" />
</mappers>
</configuration>
3、编辑applicationContext文件
注入SqlSessionFactoryBean对象,设置数据源,设置configLocation为mybatis配置文件的位置
<!-- 注入sqlSessionFactoryBean对象 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
4、注入mapper扫描器扫dao包
注入MapperScannerConfigurer
<!-- mapper扫描器的注入 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.jz.dao"/>
</bean>
5、编写dao和sql映射文件
五、SpringBoot整合MyBatis
0、两个插件
mybatisplus
easycode-mybatis-helper自动生成代码模板
1、创建项目的时候选上mybatis-framework、mysql-driver、spring data jdbc的依赖
2、编写配置文件
idea数据库环境并在properties配置数据源与mybatis的配置,可以指定datasource的实现为druid
spring.application.name=springboot02
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/studb
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#mybaits日志配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#映射文件类型
mybatis.mapper-locations=classpath:mapper/*Dao.xml
#指定实体包的别名
mybatis.type-aliases-package=com.jz.entity
3、利用插件自动生成代码模板
选择包和模板