一、Mybatis简介
1.1 简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2 持久化
持久化通俗来说就是数据持久化,就是将程序的数据在持久状态和瞬时状态转化的过程。
内存:断电即失
持久化方式:数据库、io文件持久化等。
1.3 持久层
Dao层、Service层、Controller层
1.4 为什么用Mybatis
Mybatis可以帮助我们将数据存入到数据库中。使用方便,学习成本低,相比传统的JDBC来说更简单。
优点:
- 简单易学
- 灵活
- sql和代码的分离,提高了可维护性,解耦。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
二、第一个Mybatis程序
2.1 搭建环境
1.新建项目(maven),然后删除src目录,之后把这个项目当做父工程。以后通用的依赖放在父工程的pom.xml中,子工程可以直接使用。
2.导入maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javasm</groupId>
<artifactId>MybatisPro</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>day01</module>
</modules>
<dependencies>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis框架-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3.创建Moudle,当做子工程。
4.编写mybatis的核心配置文件。
package com.javasm.utils;
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;
/**
* @Author: huerxin
* @version: 1.0
* @Date: 2022/12/13-15:26
* @Since:jdk1.8
* @Description:
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//1.指定配置文件位置
String resource = "mybatis-config.xml";
//2.读取配置文件,转成流对象
InputStream inputStream = Resources.getResourceAsStream(resource);
//3.根据流对象创建sqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象
* @return
*/
public static SqlSession getSqlSession() {
//4.工厂创建SqlSession
return sqlSessionFactory.openSession();
}
}
5.编写properties文件,配置driver、数据库账号密码等。然后在核心配置文件中引入即可。
- 在resources包中编写db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bankingdb?useSSL=false
username=root
password=root
- 在核心配置文件中引入db.properties文件
<!--引入外部配置文件-->
<properties resource="db.properties"/>
6.编写mybatis工具类
package com.javasm.utils;
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;
/**
* @Author: huerxin
* @version: 1.0
* @Date: 2022/12/13-15:26
* @Since:jdk1.8
* @Description:
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//1.指定配置文件位置
String resource = "mybatis-config.xml";
//2.读取配置文件,转成流对象
InputStream inputStream = Resources.getResourceAsStream(resource);
//3.根据流对象创建sqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象
* @return
*/
public static SqlSession getSqlSession() {
//4.工厂创建SqlSession
return sqlSessionFactory.openSession();
}
}
7.在父工程的pom.xml中添加一段代码、防止资源导出失败的问题。
<!--防止我们资源导出失败的问题-->
<!--pom.xml-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
8.CRUD
-
实体类
package com.javasm.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Sysuser {
private long uid;
private String uname;
private String upwd;
private String uemail;
private String phone;
private Date createTime;
private Date updateTime;
private String deleted;
private long rid;
public Sysuser(long uid, String uname, String upwd) {
this.uid = uid;
this.uname = uname;
this.upwd = upwd;
}
public Sysuser(String uname, String upwd) {
this.uname = uname;
this.upwd = upwd;
}
}
-
SysuserMapper接口
package com.javasm.mapper;
import com.javasm.entity.Sysuser;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Author: huerxin
* @version: 1.0
* @Date: 2022/12/13-15:19
* @Since:jdk1.8
* @Description:
*/
public interface SysuserMapper {
/**
* 查询所有的用户
* @return
*/
List<Sysuser> findList();
/**
* 添加
* @param sysuser
* @return
*/
Boolean addUser(Sysuser sysuser);
/**
* 添加
* @param uid
* @return
*/
Boolean deleteUser(@Param("uid") Integer uid);
/**
* 修改
* @param sysuser
* @return
*/
Boolean updateUser(Sysuser sysuser);
/**
* 根据id查询用户
* @param uid
* @return
*/
Sysuser findById(@Param("uid") Integer uid);
}
-
创建SysuserMapper.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.javasm.mapper.SysuserMapper">
<insert id="addUser" >
insert into sysuser(uname,upwd) values (#{uname},#{upwd});
</insert>
<update id="updateUser" >
update sysuser set uname=#{uname},upwd=#{upwd} where uid=#{uid}
</update>
<delete id="deleteUser" parameterType="int">
delete from sysuser where uid=#{uid}
</delete>
<select id="findList" resultType="Sysuser">
select * from sysuser;
</select>
<select id="findById" resultType="Sysuser">
select * from sysuser where uid=#{uid}
</select>
</mapper>
-
junit单元测试
package com.javasm.mapper;
import com.javasm.entity.Sysuser;
import com.javasm.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;
import java.util.List;
import static org.junit.Assert.*;
/**
* @Author: huerxin
* @version: 1.0
* @Date: 2022/12/13-15:24
* @Since:jdk1.8
* @Description:
*/
public class SysuserMapperTest {
/**
* 查询所有
*/
@Test
public void findList() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
List<Sysuser> sysuserList = mapper.findList();
sysuserList.forEach(System.out::println);
sqlSession.close();
}
/**
* 添加
*/
@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
Sysuser sysuser =new Sysuser("朱凯","ewrwe");
mapper.addUser(sysuser);
sqlSession.commit();
sqlSession.close();
}
/**
* 删除
*/
@Test
public void deleteUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
mapper.deleteUser(51);
sqlSession.commit();
sqlSession.close();
}
/**
* 修改
*/
@Test
public void updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
Sysuser sysuser =new Sysuser(50,"朱凯1111","ewrwe");
mapper.updateUser(sysuser);
sqlSession.commit();
sqlSession.close();
}
/**
* 根据id查用户
*/
@Test
public void findById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
Sysuser sysuser = mapper.findById(1);
System.out.println(sysuser);
sqlSession.close();
}
}
2.2 总结
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型写错了
- Maven导出资源问题 -->在pom.xml中加入上面的代码
- 写完后记得关闭,增删改记得提交事务
三、万能的Map
3.1 为什么使用map
- 使用map传参,可以任意的添加参数,在参数纵多的时候,使用起来特别方便。
接口
/**
* 用map传参
* @param map
* @return
*/
Boolean addUser2(Map<String,Object> map);
xml文件中
<insert id="addUser2">
insert into sysuser (uname,upwd,phone) values (#{uname},#{upwd},#{phone});
</insert>
juint单元测试
/**
* 用map传参添加
*/
@Test
public void addUser2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
Map<String,Object> map =new HashMap<>();
map.put("uname","qwerqwe");
map.put("upwd","werwqe");
map.put("phone","15616846843");
mapper.addUser2(map);
sqlSession.commit();
sqlSession.close();
}
- 动态sql查询的时候,也可以动态的传入参数,方便后面修改业务。
比如之前写过的金融项目,里面的动态sql就用到了map。
<where>
<if test="map!=null">
<if test="map.productName!=null and map.productName!='' ">
and b.product_name like "%"#{map.productName}"%"
</if>
<if test="map.product_secondary_classification!=null">
and b.product_secondary_classification = #{map.productSecondaryClassification}
</if>
<if test="map.auditStatus!=null">
and b.audit_status = #{map.auditStatus}
</if>
</if>
</where>
四、ORM框架
4.1 什么是ORM
ORM,==即Object-Relational Mapping(对象关系映射)==,它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法。
4.2 为什么会出现ORM思想
先从项目中数据流存储形式这个角度说起。简单拿MVC这种分层模式来说, Model作为数据承载实体。 在用户界面层和业务逻辑层之间数据实现面向对象OO形式传递。 当我们需要通过Control层分发请求把数据持久化时我们会发现。 内存中的面向对象的OO如何持久化成关系型数据中存储一条实际数据记录呢?
面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的。两者之间是不匹配的。而ORM作为项目中间件形式实现数据在不同场景下数据关系映射。对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM就是这样而来的。
![在这里插入图片描述](https://img-blog.csdnimg.cn/af0a7aaf660c437aacb6c8a092acf4fb.png#pic_center)
4.3 ORM优缺点
优势
第一:隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
第二:ORM使我们构造固化数据结构变得简单易行。在ORM年表的史前时代,我们需要将我们的对象模型转化为一条一条的SQL语句,通过直连或是DB helper在关系数据库构造我们的数据库体系。而现在,基本上所有的ORM框架都提供了通过对象模型构造关系数据库结构的功能。这相当不错。
缺点
第一:无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。
第二:面向对象的查询语言(X-QL)作为一种数据库与对象之间的过渡,虽然隐藏了数据层面的业务抽象,但并不能完全的屏蔽掉数据库层的设计,并且无疑将增加学习成本。
第三:对于复杂查询,ORM仍然力不从心。虽然可以实现,但是不值的。视图可以解决大部分calculated column,case ,group,having,order by, exists,但是查询条件(a and b and not c and (d or d))就解决不了。
4.4 mybatis为什么是半自动ORM框架
Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。==而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,==所以,称之为半自动 ORM 映射工具。
4.5 Mybatis是否支持延迟加载
支持。Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载, association指的就是多对一, collection 指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载LazyLoadingEnabled=true|false。
他的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送实现保存好的查询关联B对象的SQL,把B查询上来,然后调用a.setB(b),于是a的对象b熟悉就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
4.6 库和框架的区别
库和框架都是一种有别于软件、面向程序开发者的产品形式。正因为如此,也有很多人误以为库就是框架,或者认为指定语言的库就是框架。
库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫类库。面向过程的代码组织形式而成的库也叫函数库。在函数库中的可直接使用的函数叫库函数。开发者在使用库的时候,只需要使用库的一部分类或函数,然后继续实现自己的功能。
==框架则是为解决一个(一类)问题而开发的产品,==框架用户一般只需要使用框架提供的类或函数,即可实现全部功能。可以说,框架是库的升级版。开发者在使用框架的时候,必须使用这个框架的全部代码。
五、XML配置解析
5.1 顶层结构
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
5.2 属性(properties)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
编写配置文件db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/4032?useSSL=false
username=root
password=root
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
注意:如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载。
- 首先读取在 properties 元素体内指定的属性。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
5.3 设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项设置的含义、默认值等。
设置名 |
描述 |
mapUnderscoreToCamelCase |
是否开启驼峰命名自动映射 |
logImpl |
开启打印日志,如log4j |
其他的设置用的比较少,可以直接去官网看。https://mybatis.org/mybatis-3/zh/getting-started.html
5.4 类型别名(typeAliases)
类型别名是为了Java类型设置一个短的名字。存在的意义仅用来减少类完全限定名的冗余。
-
给实体类起别名
<!--可以给实体类起别名-->
<typeAliases>
<typeAlias type="com.javasm.entity.User" alias="user"/>
</typeAliases>
-
可以扫描实体类的包,默认别名就是这个类的别名
<!--给实体类取别名-->
<typeAliases>
<!--<typeAlias type="com.javasm.system.entity.AdminInfo" alias="AdminInfo"/>-->
<!--扫描实体类的包,他的默认别名就为这个类的别名,首字母小写-->
<package name="com.javasm.system.entity"/>
<package name="com.javasm.unicorn.entity"/>
<package name="com.javasm.product.entity"/>
</typeAliases>
第二种方法也可以diy别名,但需要在实体类上加上注解,如@Alias(“asuser”),那这个类的别名就是这个注解里面的值。
5.5 环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。
5.6 其他
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- MyBatis-plus
- mybatis-generator-core
- 通用mapper
5.7 映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉MyBatis 到哪里去找映射文件。
-
使用相对路径的资源引用 【推荐使用】
<mappers>
<mapper resource="com/javasm/dao/UserMapper.xml"/>
</mappers>
-
使用映射器接口实现类的完全限定名
<mppers>
<mpper class="com.javasm.dao.UserMapper"/>
</mppers>
-
将包内的映射器接口全部注册为映射器
<mppers>
<package name="com.javasm.dao"/>
</mppers>
总结:方式二和方式三的接口和Mapper配置文件必须同名,并且在同一个包下。
5.8 作用域(Scope)和生命周期
理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
1、SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory,就不再需要它了。SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
2、SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,==没有任何理由丢弃它或重新创建另一个实例。==使用的最佳实践是在应用运行期间不要重复创建多次,最简单的就是使用单例模式或者静态单例模式。
3、SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。用完之后赶紧关闭。
六、日志
6.1 日志工厂
如果一个数据库操作,出现了异常,我们需要排错。日志是最好的帮手。过去我们使用sout、debug,而现在可以使用日志工厂。
6.2 LOG4J
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台,文件,GUI组件。
我们可以空指每一条日志的输出格式,通过定义每一条日志信息的级别,哦我们可以更加细致的空指日志的生成过程。
可以通过配置文件来配置,不需要修改代码。
1、导包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、创建log4j.properties
### 设置###
log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =D://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
3、在核心配置文件中,配置注册日志文件。
<!--日志文件-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
七、多对一
7.1 数据库
-- 员工表
drop table if exists tb_emp;
create table tb_emp(
id int(30) primary key auto_increment,
e_name varchar(30),
age int(30),
did int(30)
);
-- 部门表
drop table if exists tb_dept;
create table tb_dept(
id int(30) primary key auto_increment,
d_name varchar(30)
);
insert into tb_emp values(null,"朱凯",23,1);
insert into tb_emp values(null,"刘亚国",20,1);
insert into tb_emp values(null,"吴亦凡",30,2);
insert into tb_emp values(null,"李云迪",32,2);
insert into tb_emp values(null,"蔡徐坤",26,2);
insert into tb_emp values(null,"吴京",29,3);
insert into tb_emp values(null,"王宝强",36,3);
insert into tb_dept values(null,"销售部");
insert into tb_dept values(null,"开发部");
insert into tb_dept values(null,"前端部");
7.2 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private Integer id;
private String dName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private Integer id;
private String eName;
private Integer age;
private Dept dept; --多个员工属于一个部门
}
7.3 接口
public interface EmpMapper {
/**
* 按照结果嵌套处理
* @return
*/
List<Emp> selectEmp();
/**
* 按照查询嵌套处理
* @return
*/
List<Emp> selectEmp2();
}
7.4 Mapper.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.javasm.mapper.EmpMapper">
<!--按照结果嵌套处理-->
<select id="selectEmp" resultMap="EmpAndDept">
select e.id id,e.e_name ename,e.age age,d.d_name dname
from tb_emp e,tb_dept d where d.id=e.did ;
</select>
<!--字段需要一一对应-->
<!--结果封装,将查询出来的列封装到对象属性中-->
<resultMap id="EmpAndDept" type="Emp">
<result column="id" property="id"/>
<result column="ename" property="eName"/>
<result column="age" property="age"/>
<association property="dept" column="did" javaType="Dept">
<result column="id" property="id"/>
<result column="dname" property="dName"/>
</association>
</resultMap>
<!--按照查询嵌套处理-->
<select id="selectEmp2" resultMap="EmpAndDept2">
select * from tb_emp
</select>
<select id="selectDept" resultType="Dept">
select * from tb_dept where id=#{did};
</select>
<resultMap id="EmpAndDept2" type="Emp">
<result column="id" property="id"/>
<result column="e_name" property="eName"/>
<result column="age" property="age"/>
<!--复杂的属性,我们需要单独出来 对象:association 集合:collection-->
<association property="dept" javaType="Dept" select="selectDept" column="did"/>
</resultMap>
</mapper>
7.5 juint单元测试
public class EmpMapperTest {
/**
* 按照结果嵌套处理
*/
@Test
public void selectEmp() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> emps = mapper.selectEmp();
for (Emp emp : emps) {
System.out.println(emp);
}
sqlSession.close();
}
/**
* 按照查询嵌套处理
*/
@Test
public void selectEmp2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> emps = mapper.selectEmp2();
for (Emp emp : emps) {
System.out.println(emp);
}
sqlSession.close();
}
}
7.6 测试结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/424640128b36484398ad787e54ee6810.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/3277e7b77bf040c0b074d6276b6d5606.png#pic_center)
7.7 总结
显而易见,这两种方式都能处理多对一的问题。按照查询嵌套处理就是子查询。按照结果嵌套处理就是连表查询。
多对一 ------>【关联】 -------> 【association 】
一对多 ------> 【集合】--------> 【collection 】
八、一对多
8.1 数据库
数据库和上面的一样。
8.2 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private Integer id;
private String eName;
private Integer age;
private Integer did;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private Integer id;
private String dName;
private List<Emp> emps;
}
8.3 接口
public interface DeptMapper {
/**
* 按照结果嵌套处理
* @param id
* @return
*/
Dept findDept(int id);
/**
* 按照查询嵌套处理
* @param id
* @return
*/
Dept findDept2(int id);
}
8.4 Mapper.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.javasm.mapper.DeptMapper">
<!--按结果嵌套处理-->
<select id="findDept" resultMap="DeptAndEmp">
select d.id did,d.d_name dname,e.id eid,e.e_name ename,e.age age,e.did did
from tb_dept d,tb_emp e where d.id=e.did;
</select>
<resultMap id="DeptAndEmp" type="Dept">
<result column="did" property="id"/>
<result column="dname" property="dName"/>
<collection property="emps" ofType="Emp">
<result column="eid" property="id"/>
<result column="ename" property="eName"/>
<result column="age" property="age"/>
<result column="did" property="did"/>
</collection>
</resultMap>
<!--按查询结果嵌套处理-->
<select id="findDept2" resultMap="DeptAndEmp2">
select * from tb_dept where id=#{id};
</select>
<select id="getEmp" resultType="Emp">
select * from tb_emp where did=#{did};
</select>
<resultMap id="DeptAndEmp2" type="Dept">
<result column="id" property="id"/>
<result column="d_name" property="dName"/>
<collection property="emps" javaType="ArrayList" ofType="Emp" select="getEmp" column="id"/>
</resultMap>
</mapper>
7.5 juint单元测试
public class DeptMapperTest {
/**
* 按照结果嵌套处理
*/
@Test
public void findDept() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findDept(1);
System.out.println(dept);
sqlSession.close();
}
/**
* 按照查询嵌套处理
*/
@Test
public void findDept2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findDept2(1);
System.out.println(dept);
sqlSession.close();
}
}
7.6 测试结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/ae78a5d08ee04f97bbe254c8ef26a592.png#pic_center)
即员工作为一个集合存储在对象中。
7.7 总结
1、关系
多对一 ------>【关联】 -------> 【association 】
一对多 ------> 【集合】--------> 【collection 】
2、JavaType & ofType
JavaType 用来指定实体类中属性的类型。
ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型。
九、使用注解开发
9.1 面向接口编程
根本原因:解耦,可扩展,提高复用,分层开发中,上层开发中,上层不用管具体的实现,大家都会遵守共同的标准,使得开发变得容易,规范性更好。
9.2 注解CRUD
public interface SysroleMapper {
/**
* 查询所有的角色
* @return
*/
@Select("select * from sysrole")
List<Sysrole> showAllRole();
/**
* 根据id查询角色
* @param id
* @return
*/
@Select("select * from sysrole where rid = #{rid} ")
Sysrole showRoleById(Integer id);
/**
* 添加角色
* @param sysrole
* @return
*/
@Insert("insert into sysrole(rname,rdec) values (#{rname},#{rdec}) ")
Integer addRole(Sysrole sysrole);
/**
* 修改角色
* @param sysrole
* @return
*/
@Update("update sysrole set rname=#{rname},rdec=#{rdec} where rid=#{rid} ")
Integer updateRole(Sysrole sysrole);
/**
* 删除角色
* @param id
* @return
*/
@Delete("delete from sysrole where rid=#{rid}")
Integer deleteRole(Integer id);
}
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java
注解不仅力不从心,还会让本就复杂的 SQL 语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
9.3 @param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
- 我们在SQL中引用的就是我们这里的@Param()中设定的属性名
9.4 #{} 和 ${} 区别
- #{}可以防止sql注入 安全
- ${} 不可以防止sql注入 不安全
9.5 mybatis的详细执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2L6EjTY-1671027643438)(E:\JAVA核心笔记\img\watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP56CBXw==,size_20,color_FFFFFF,t_70,g_se,x_16)]
十、模糊查询
10.1 接口
/**
* 根据姓名模糊查询
* @param name
* @return
*/
List<Sysuser> findUserByLikeName(String name);
/**
* 多个参数模糊查询 -->注解传参
* @param name
* @param pwd
* @return
*/
List<Sysuser> findUserByLikeNameAndPwd(@Param("name") String name,@Param("pwd") String pwd);
/**
* 多个参数模糊查询 ---->map
* @param map
* @return
*/
List<Sysuser> findUserByLikeMap(Map<String,Object> map);
/**
* 多个参数模糊查询 通过QueryCriteria对象传参
* @param queryCriteria
* @return
*/
List<Sysuser> findUserByLikeQueryCriteria(QueryCriteria queryCriteria);
10.2 测试类
/**
* 模糊查询
*/
@Test
public void findUserByLikeName() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
List<Sysuser> sysuserList = mapper.findUserByLikeName("张二");
sysuserList.forEach(sysuser -> System.out.println(sysuser));
sqlSession.close();
}
/**
* 多个参数模糊查询 使用注解
*/
@Test
public void findUserByLikeNameAndPwd() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
List<Sysuser> sysuserList = mapper.findUserByLikeNameAndPwd("张", "2");
sysuserList.forEach(sysuser -> System.out.println(sysuser));
sqlSession.close();
}
/**
* 多个参数模糊查询 通过map传参
*/
@Test
public void findUserByLikeMap() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
Map<String,Object> map =new HashMap<>();
map.put("name","张");
map.put("pwd","2");
List<Sysuser> sysuserList = mapper.findUserByLikeMap(map);
sysuserList.forEach(sysuser -> System.out.println(sysuser));
sqlSession.close();
}
/**
* 多个参数模糊查询 通过QueryCriteria对象传参
*/
@Test
public void findUserByLikeQueryCriteria() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
SysuserMapper mapper = sqlSession.getMapper(SysuserMapper.class);
QueryCriteria queryCriteria =new QueryCriteria("张","2");
List<Sysuser> sysuserList = mapper.findUserByLikeQueryCriteria(queryCriteria);
sysuserList.forEach(sysuser -> System.out.println(sysuser));
sqlSession.close();
}
10.3 mapper.xml文件
<!--<select id="findUserByLikeName" resultType="com.javasm.entity.Sysuser">-->
<!--select * from sysuser where uname like ‘%’#{uname}‘%’;-->
<!--</select>-->
<select id="findUserByLikeName" resultType="com.javasm.entity.Sysuser">
select * from sysuser where uname like concat(concat('%',#{uname}),'%') ;
</select>
<insert id="addUser2">
insert into sysuser (uname,upwd,phone) values (#{uname},#{upwd},#{phone});
</insert>
<select id="findUserByLikeNameAndPwd" resultType="com.javasm.entity.Sysuser">
select * from sysuser where uname like concat(concat('%',#{name}),'%') and upwd like concat(concat('%',#{pwd}),'%')
</select>
<select id="findUserByLikeMap" resultType="com.javasm.entity.Sysuser">
select * from sysuser where uname like concat(concat('%',#{name}),'%') and upwd like concat(concat('%',#{pwd}),'%')
</select>
<select id="findUserByLikeQueryCriteria" resultType="com.javasm.entity.Sysuser">
select * from sysuser where uname like concat(concat('%',#{name}),'%') and upwd like concat(concat('%',#{pwd}),'%')
</select>
10.4 总结
单个参数的模糊查询直接传入参数就行。
有两种常用的方式
1、‘%’#{uname}‘%’ -->常用
2、concat(concat(‘%’,#{uname}),‘%’) -->常用
多个参数的模糊查询有三种方式。
1、注解传参 --> @Param(“name”)
2、用map传参 --> Map<String,Object> map
3、通过对象传参 --> QueryCriteria queryCriteria
十一、动态sql
11.1 搭建环境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT'浏览器'
)
11.2 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private Integer views;
}
11.3 IF的使用
1、接口
/**
* 通过IF查询
* @param map
* @return
*/
List<Blog> queryBlogIF(Map map);
2、BlogMapper.xml文件
<!--if语句-->
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="title !=null">
and title = #{title}
</if>
<if test="author !=null">
and author = #{author}
</if>
</where>
</select>
3、测试
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("title","Spring");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
4、总结
IF语句最好的使用方法是和WHERE一起使用,满足条件即可拼接上,可以动态的拼接上查询条件。
11.4 choose(when otherwise) 的使用
1、接口
/**
* Choose的查询
* @param map
* @return
*/
List<Blog> queryBlogChoose(Map map);
2、BlogMapper.xml文件
<!--从上到下下依次匹配 类似于case加break-->
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<choose>
<when test="title !=null">
title = #{title}
</when>
<when test="author !=null">
and author = #{author}
</when>
<otherwise>
and views=#{views}
</otherwise>
</choose>
</where>
</select>
3、测试
@Test
public void queryBlogChoose(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("title","Spring");
map.put("author","狂神说");
map.put("views",9989);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
4、总结
从多个条件中选择一个使用,类似于java中的switch语句。
11.5 set 的使用
1、接口
/**
* 更新
* @param map
* @return
*/
int updateBlog(Map map);
2、BlogMapper.xml文件
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title !=null">
title = #{title},
</if>
<if test="author !=null">
author = #{author},
</if>
<if test="views !=null">
views=#{views},
</if>
</set>
where id=#{id};
</update>
3.测试
@Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("id","a0552f7699214a6685d17ca850dd5563");
map.put("title","帅哥养成记");
map.put("author","小胡");
map.put("views",8888);
mapper.updateBlog(map);
sqlSession.commit();
sqlSession.close();
}
4、总结
set是满足条件则修改,可以动态的修改sql语句。
11.6 foreach的使用
1、接口
//查询1,2,3好记录的博客
List<Blog> queryBlogForEach(Map map);
2、BlogMapper.xml文件
<select id="queryBlogForEach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" open="(" close=")" separator="or" item="id">
id=#{id}
</foreach>
</where>
</select>
3、测试
@Test
public void queryBlogForEach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(4);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForEach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
4、测试结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/f91f78fbae2247b881a88bf623c9d31a.png#pic_center)
5、总结
collection=“ids” 为要遍历的集合
item=“id” 为集合里的数据
separator=“or” 为分割符
open 和 close 为开头和结尾的拼接
简单的来说就是可以动态的查询语句。
十二、缓存
12.1简介
缓存是存在内存中的临时数据,将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题。
使用缓存的原因是,==减少和数据库的交互次数,减少系统开销,提高系统效率。==经常查询并且不经常修改的数据可以使用缓存。
12.2 mybatis缓存
1、一级缓存也叫本地缓存: SqlSession
- 与数据库同义词会话期间查询到的数据会放在本地缓存中;
- 以后如果需要直接到本地缓存中拿,没必要再去查询数据库;
缓存失效的情况:
1、查询不同的东西。
2、执行了增删改操作,可能改变原来的数据,必定会刷新缓存。
3、查询不同的Mapper.xml。
4、手动清理缓存 : sqlSession.clearCache() 。
5、关闭缓存: sqlSession.close() 。
总的来说,一级缓存就是个map。仅在一次会话中有效,即从拿到连接对象到关闭连接的这个区间中有效。
2、二级缓存
1、开启全局缓存
<!--显示开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
2、在要使用二级缓存的Mapper中开启
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
3、参数
<cache
eviction="FIFO" //清除策略
flushInterval="60000" //刷新时间间隔
size="512" //最多可存储结果的对象
readOnly="true"/> //是否只读
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
-
LRU
– 最近最少使用:移除最长时间不被使用的对象。
-
FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。
-
SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。
-
WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
4、总结
- 只要开启二级缓存,在同一个Mapper下就会有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交后,或者关闭的时候,才会提交二级缓存中!!
(img-wx4i3JSn-1671027643438)]
5、总结
collection=“ids” 为要遍历的集合
item=“id” 为集合里的数据
separator=“or” 为分割符
open 和 close 为开头和结尾的拼接
简单的来说就是可以动态的查询语句。
十二、缓存
12.1简介
缓存是存在内存中的临时数据,将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题。
使用缓存的原因是,==减少和数据库的交互次数,减少系统开销,提高系统效率。==经常查询并且不经常修改的数据可以使用缓存。
12.2 mybatis缓存
1、一级缓存也叫本地缓存: SqlSession
- 与数据库同义词会话期间查询到的数据会放在本地缓存中;
- 以后如果需要直接到本地缓存中拿,没必要再去查询数据库;
缓存失效的情况:
1、查询不同的东西。
2、执行了增删改操作,可能改变原来的数据,必定会刷新缓存。
3、查询不同的Mapper.xml。
4、手动清理缓存 : sqlSession.clearCache() 。
5、关闭缓存: sqlSession.close() 。
总的来说,一级缓存就是个map。仅在一次会话中有效,即从拿到连接对象到关闭连接的这个区间中有效。
2、二级缓存
1、开启全局缓存
<!--显示开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
2、在要使用二级缓存的Mapper中开启
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
3、参数
<cache
eviction="FIFO" //清除策略
flushInterval="60000" //刷新时间间隔
size="512" //最多可存储结果的对象
readOnly="true"/> //是否只读
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
-
LRU
– 最近最少使用:移除最长时间不被使用的对象。
-
FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。
-
SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。
-
WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
4、总结
- 只要开启二级缓存,在同一个Mapper下就会有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交后,或者关闭的时候,才会提交二级缓存中!!
![在这里插入图片描述](https://img-blog.csdnimg.cn/d53c01b29e45435a88f08d038e4a0b1f.png)