MyBatis
动态代理的核心就是对一个方法的增强。
mybatis的crud基本流程:
// 1、创建一个SqlSessionFactory的 建造者 ,用于创建SqlSessionFactory
// SqlSessionFactoryBuilder中有大量的重载的build方法,可以根据不同的入参,进行构建
// 极大的提高了灵活性,此处使用【创建者设计模式】
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 2、使用builder构建一个sqlSessionFactory,此处我们基于一个xml配置文件
// 此过程会进行xml文件的解析,过程相对比较复杂
SqlSessionFactory sqlSessionFactory = builder.build(Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml"));
// 3、通过sqlSessionFactory获取另一个session,此处使用【工厂设计模式】
SqlSession sqlSession = sqlSessionFactory.openSession();
1、创建一个SqlSessionFactory的 建造者 ,用于创建SqlSessionFactory
2、使用builder构建一个sqlSessionFactory,此处我们基于一个xml配置文件
3、通过sqlSessionFactory获取另一个session,此处使用【工厂设计模式】
4、一个sqlsession就是一个会话,可以使用sqlsession对数据库进行操作,原理后边会讲。
1 . lombok的使用:
-
@AllArgsConstructor:生成全参构造器。
-
@NoArgsConstructor:生成无参构造器。
-
@Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法;作用于成员变量上,生成该成员变量的getter/setter方法。可以设定访问权限及是否懒加载等。
-
@Data:作用于类上,是以下注解的集合:@ToString @EqualsAndHashCode @Getter @Setter @RequiredArgsConstructor
-
@Log:作用于类上,生成日志变量。针对不同的日志实现产品,有不同的注解。
-
@Builder:使用创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。
-
@@Slf4j:
@Slf4j//加上这个就不需要我们手动写: //public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class); public class TestMybatis { //public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class); @Test public void testSqlSession() throws IOException { log.debug("debug"); } }
引入依赖:并在Setings中搜索plugings,搜索lombok将其下载。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
}
这样子我们就不用写:getter,setter,构造器,toString()之类的方法了。它已经给我们写好了。我们直接调用即可
2 . sqlSession详解:
mybatis-config.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>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
具体实现类:
@Slf4j//加上这个就不需要我们手动写:
//public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class);
public class TestMybatis {
//public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class);
@Test
public void testSqlSession() throws IOException {
User user = new User(1,"cc","12");
String resource = "mybatis-config.xml";
//通过该路劲拿到一个流
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
//statement是sql的声明
//首先找到mybatis-config.xml文件,解析完mybatis-config.xml自身后,
// 发现mybatis-config.xml内部配置了一个mappers,
// mappers内部配置了一个UserMapper.xml,找到了UserMapper.xml文件
//,解析UserMapper.xml文件后找到了一个叫user的命名空间
//在该空间找到一个sql语句 ,该sql语句的名字是select,该id的值为User类的全限定名,
// 将id和id的值存到List中
//从list中拿到user命名空间中的select
List<Object> objects = session.selectList("user.select");
log.debug("result is [{}]",objects);
//result is [[User(id=1, username=itnanls, password=123456),
// User(id=2, username=itlils, password=abcdef),
// User(id=3, username=ydlclass, password=987654)]]
}
}
}
3 . 动态mapper的实现原理:
我们线创建一个用户相关的接口和实现类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kMwPV6FV-1681790306034)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664613981586.png)]
public interface UserMapper {
//查询所有的用户
List<User> selectAll();
}
具体实现类:
public class UserMapperimpl implements UserMapper{
@Override
public List<User> selectAll() {
String resource = "mybatis-config.xml";
//通过该路劲拿到一个流
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
throw new RuntimeException(e);
}
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//这个对象是核心,一个工程的数据库相关的操作都死在围绕sqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
List<Object> objects = session.selectList(UserMapper.class.getName()+"."+"selectAll");
System.out.println(objects);
}
return null;
}
public static void main(String[] args) {
UserMapper userMapper = new UserMapperimpl();
userMapper.selectAll();
//[User(id=1, username=itnanls, password=123456),
// User(id=2, username=itlils, password=abcdef),
// User(id=3, username=ydlclass, password=987654)]
}
}
由于我们使用反射来得到接口类名,并用此来作为命名空间,sql语句查询所有用户作为内容,所以我们要将配置文件UserMapper.xml的内容修改为对应的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMbvdxI1-1681790306035)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664614264816.png)]
- esultType:指定返回类型,查询是有结果的,结果啥类型,你得告诉我
- parameterType:指定参数类型,查询是有参数的,参数啥类型,你得告诉我
- id:指定对应的方法映射关系,就是你得告诉我你这sql对应的是哪个方法
- #{id}:sql中的变量,要保证大括号的变量必须在User对象里有
- #{}:占位符,其实就是咱们的【PreparedStatement】处理这个变量,mybatis会将它替换成
?
以上便是mybatis的动态mapper代理的实现原理,我们在此原理上使用sqlSession进行运用:使其更加简单
sqlSession.getMapper(UserMapper.class);
帮我们生成一个代理对象,该对象实现了这个接口的方法,具体的数据库操作比如建立连接,创建statment等重复性的工作交给框架来处理,唯一需要额外补充的就是sql语句了,xml文件就是在补充这个描述信息,比如具体的sql,返回值的类型等,框架会根据命名空间自动匹配对应的接口,根据id自动匹配接口的方法,不需要我们再做额外的操作。
@Slf4j//加上这个就不需要我们手动写:
//public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class);
public class TestMybatis {
SqlSessionFactory sqlSessionFactory = null;
@Before//使用该注解,程序已启动就会执行这里面的内容
public void before() throws IOException {
String resource = "mybatis-config.xml";
//通过该路劲拿到一个流
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//这个对象是核心,一个工程的数据库相关的操作都死在围绕sqlSessionFactory
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
//public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class);
@Test
public void testSqlSession() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
//动态代理生成了一个mapper对象
//获得一个代理对象,使用的jdk的Proxy类,并且代理对象实现了UserMapper的接口
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
log.debug("users is [{}]",users);
//users is [[User(id=1, username=itnanls, password=123456),
// User(id=2, username=itlils, password=abcdef),
// User(id=3, username=ydlclass, password=987654)]]
}
}
}
select查询单个用户:
public interface UserMapper {
//查询所有的用户
List<User> selectAll();
//查询某个用户
User selectOne(int id);
}
我们将配置文件UserMapper.xml配置修改:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8g1nuR7n-1681790306035)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664617179144.png)]
具体实现类:
@Slf4j//加上这个就不需要我们手动写:
//public static final Logger logger = LoggerFactory.getLogger(TestMybatis.class);
public class TestMybatis {
SqlSessionFactory sqlSessionFactory = null;
@Before//使用该注解,程序已启动就会执行这里面的内容
public void before() throws IOException {
String resource = "mybatis-config.xml";
//通过该路劲拿到一个流
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//这个对象是核心,一个工程的数据库相关的操作都死在围绕sqlSessionFactory
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testFindById() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
//动态代理生成了一个mapper对象
//获得一个代理对象,使用的jdk的Proxy类,并且代理对象实现了UserMapper的接口
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectOne(1);
log.debug("users is [{}]",user);
//users is [User(id=1, username=itnanls, password=123456)]
}
}
1 . 占位符#{}的注意事项 :
#{}是占位符,$ {}是字符串拼接。
(1)按照多个参数操作的情况:
已知UserMapper接口中有方法selectUser,
@Test
public User selectUser() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUser("cc","123");
log.debug("users is [{}]",user);
return user;
}
}
UserMapper.xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rC2PYXhd-1681790306036)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664618186488.png)]
运行报错,这是因为在xml配置文件中的占位符#{}中的只是识别的数据的类型,并不能直接通过名字来识别,所以报错。
我们对接口进行修改:
意思就是username的值会传到Param中的username中,(传到xml文件中名为username的),password的值会传到Param中的password中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IRWB8XZ9-1681790306036)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664618657904.png)]
此时xml文件能够识别到参数的内容查寻到相关用户信息
(2)如果按照类对象User来进行查询时:
UserMapper接口中:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0JydBfuo-1681790306036)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664619648001.png)]
配置文件:UserMapper.xml:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-spqdEumo-1681790306037)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664619681622.png)]
具体实现类:
@Test
public void selectOneUser() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
//先封装User
User user = new User(1, "cc", "123");
User param = mapper.selectOneUser(user);
log.debug("users is [{}]",param);
}
}
此时正常运行,因为user对象本身就包含了username和password,所以不用使用@Param
(3)按照Map来进行操作时
已知UserMapper接口中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHY008qx-1681790306037)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664620122346.png)]
UserMapper.xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqqZRh89-1681790306037)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664620146919.png)]
具体实现类:
@Test
public void selectByMap() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
//新建一个Map并添加数据
Map<String,Object> map = new HashMap<>();
map.put("username","cc");
User param = mapper.selectByMap(map);
log.debug("users is [{}]",param);
//users is [User(id=1, username=itnanls, password=123456)]
}
}
原理同上,因为该map中已经包含了username,password的内容不需要注解去指定。
(4)模糊查询的格式问题:
UserMapper接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5upMZQvS-1681790306038)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664623049508.png)]
UserMapper.xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBQO21Lz-1681790306038)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664623076267.png)]
具体实现类:
@Test
public void selectByUsername() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
//解决sql语句模糊查询:like username like 'i';的格式问题
List<User> param = mapper.selectByUsername("%"+"i"+"%");
log.debug("users is [{}]",param);
//users is [User(id=1, username=itnanls, password=123456)]
}
}
(5)增,修改和删除操作:
<insert id="insert" >
insert into user (id,username,password) values (#{id},#{username},#{password})
</insert>
<update id="update" >
update `user` set username = #{username},password = #{password} where id = #{id}
</update>
<delete id="delete" >
delete from `user` where id = #{id}
</delete>
接口:
int insert(User user);
int update(User user);
int delete(User user);
具体实现:
@Test
public void insertUser() {
try (SqlSession session = sqlSessionFactory.openSession()) {
try{
UserMapper mapper = session.getMapper(UserMapper.class);
int cc = mapper.insert(new User(133, "cc", "123"));
log.debug("afftectrow is [{}]",cc);
//因为sqlSession不能自动提交事务,所以我们后台插入数据后,
// mysql中并没有成功加数据插入,需要手动提交
session.commit();
}catch (Exception e){
log.error("发生了异常",e);
//发生异常后,事务回滚
session.rollback();
}
}
}
@Test
public void updateUser() {
//添加上true的参数,则代表自动提交事务
try (SqlSession session = sqlSessionFactory.openSession(true)) {
UserMapper mapper = session.getMapper(UserMapper.class);
int c2 = mapper.update(new User(3,"cgboy","456"));
log.debug("afftectrow is [{}]",c2);
}
}
@Test
public void deleteUser() {
//添加上true的参数,则代表自动提交事务
try (SqlSession session = sqlSessionFactory.openSession(true)) {
UserMapper mapper = session.getMapper(UserMapper.class);
int c2 = mapper.delete(new User(134,"cgboy","456"));
log.debug("afftectrow is [{}]",c2);
}
}
3 . 别名的使用:
我们每次写新的sql时在UserMapper.xml中配置文件都要重复的写上数据库中表对应目标的类,这样太麻烦了“:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RCHKyYPA-1681790306038)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664783448052.png)]
所以我们在mybatis-config.xml中给这个数据库中表对应的目标类配置别名:
查看配置的位置顺序:点击下列信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sqe7vsKN-1681790306038)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665296952141.png)]
进入后显示各个标签设置的先后顺序得知别名要配在settings后边,environment前面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4WohtnXS-1681790306039)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665297038894.png)]
配置信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yUH9gZoK-1681790306039)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664783511919.png)]
然后我们在UserMapper.xml中只需要写,就更方便了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UtkeK3Io-1681790306039)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664783562121.png)]
但是这样依然很麻烦,因为当有很多类时,我们就要配很多类的别名,所以我们用包名来进行配别名,这样整个包
下的类都会有别名,不需要我们自己配置了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gx0NZ4XL-1681790306040)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664783769520.png)]
在该包下的所有类都会自动有别名。我们只需要在UserMapper.xml中写对应的数据库表名就行:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQqKwoTS-1681790306040)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664785350906.png)]
4 . 切换数据源:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tk9Jbkr8-1681790306040)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665655409815.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XTPyJrE6-1681790306040)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665655474466.png)]
引入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
mybatis-config.xml配置:使其测试时通过德鲁伊连接池进行操作
<environment id="test"> <!--id为test代表为测试环境-->
<transactionManager type="JDBC"/>
<dataSource type="com.cgboy.datasource.DruidDataSourceFactory">
<property name="druid.driverClassName" value="${driver}"/>
<property name="druid.url" value="${url}"/>
<property name="druid.username" value="${username}"/>
<property name="druid.password" value="${password}"/>
</dataSource>
</environment>
获得德鲁伊数据源:
public class DruidDataSourceFactory extends PooledDataSourceFactory {
private Properties properties;
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public DataSource getDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.configFromPropety(properties);
return druidDataSource;
}
}
测试:
@Test
public void testSqlSession() throws IOException {
try (SqlSession session = sqlSessionFactory.openSession()) {
//动态代理生成了一个mapper对象
//获得一个代理对象,使用的jdk的Proxy类,并且代理对象实现了UserMapper的接口
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
log.debug("users is [{}]",users);
//users is [[User(id=1, username=itnanls, password=123456),
// User(id=2, username=itlils, password=abcdef),
// User(id=3, username=ydlclass, password=987654)]]
}
}
5 . 切换日志实现:
配置文件logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="%d{yyyy-MM-dd HH:mm:ss} %c [%thread] %-5level %msg%n"/>
<property name="log_dir" value="d:/logs" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<!--日志格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!--日志输出路径-->
<file>${log_dir}/sql.log</file>
</appender>
<root level="ALL">
<appender-ref ref="console"/>
</root>
<!--输出到文件,和控制台-->
<logger name="mybatis.sql" level="debug" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</logger>
</configuration>
引入依赖:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
mybatis-config.xml中设置使用slf4j的日志实现:
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
在我们测试类运行后显示的便是slf4j的日志实现格式。并在指定文件路径新建了一个sql.log的日志文件记录
5 . reslutmap:
引入原因:
我们在数据库中将对应user表的username列修改为user_name,保存后,在idea中测试遍历表中所有数据时,发现id和password的值都能显示,username拿到的值却为null,这是因为我们mybatis是使用的反射的原理拿到了对应的user类,从而从user类中id,username,password找到数据库user表对应的数据。而我们将数据库user表的username修改为user_name,导致了mybatis找不到了user_name的值,所以为null。
这种情况我们可以在UserMapper.xml文件中将sql语句的user_name列名加上别名来解决:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFL1KnV7-1681790306041)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664864779635.png)]
但是这样太麻烦了。我们是使用resultMap的映射关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MdF1osSa-1681790306041)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664865030299.png[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d7PV5oQR-1681790307024)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664980423914.png)]]
更方便的操作:在mybayis.config.xml主配置文件下开启驼峰命名规则:
<!--开启驼峰命名规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
数据库user表中user_name的列名,在mybatis中User类的userName会被自动识别到数据库的user_name。
有了驼峰式命名的配置信息后,前面我们写的都可以不要,也能映射到对应的值。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZ50u4Ec-1681790306042)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664865733652.png)]
照样能运行拿到对应的值。
6 . 动态sql查询语句(必须学会):
(1)where和if标签:
MyBatis提供了对SQL语句动态的组装能力,大量的判断都可以在 MyBatis的映射XML文件里面配置,以达到许多我们需要大量代码才能实现的功能,大大减少了我们编写代码的工作量。
如下在UserMapper.xml配置中“:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uF9qxmD0-1681790306042)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664870662168.png)]
在where标签内保证了会在where标签内运行,它实际上是这种格式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GbzH8CWp-1681790306042)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664872837236.png)]
意思是保证where后我们加了一个1=1
的绝对true的语句,目的是为了防止语句错误,变成SELECT * FROM student WHERE
这样where后没有内容的错误语句。这样会写看着有点奇怪,所以我们直接将内容包在标签内。
在实现类中:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-THTxcr7X-1681790306042)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664870694105.png)]
看输出的sql语句:为null都不显示出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XW11fhD9-1681790306043)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664870719807.png)]
如果我们把参数更改:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7wS1DsRa-1681790306043)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664870790287.png)]
此时观察输出sql语句:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T85Vqwl5-1681790306043)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664870817685.png)]
(2)trim标签:
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着我们需要去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
把原本的前缀编程一个新的前缀。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRgPGzUE-1681790306043)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664873189051.png)]
将and改成了where(只是为了示范,其实这样做了sql语句就错了)
已知id,username为null,输出不显示,只显示password查看输出的sql语句证实:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iv4KJ44I-1681790306044)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664873270193.png)]
(3)choose,when(if),otherwise(else)标签:
类似 if … else if 语法:在choose标签内执行,when就是if,otherwise就是else
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bcdH8azB-1681790306044)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664873933613.png)]
查询语句动态sql:
以上就是有关查询语句的动态sql的解释,我们最常用的是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SDTlvGKf-1681790306045)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664874141027.png)]
7 . 动态sql修改语句:
xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRL35vgn-1681790306045)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664875674608.png)]
具体实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WrRzAJ4E-1681790306045)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664875614167.png)]
输出结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iS1AeNu6-1681790306045)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664875630623.png)]
8 . foreach标签:
(1)批量删除和遍历:
UserMapper.xml文件配置:
删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gejskTCP-1681790306046)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664877395374.png)]
遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9IdJglv-1681790306046)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664877332181.png)]
UserMapper接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2MTcXKI-1681790306046)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664877369974.png)]
具体实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEe2GwRy-1681790306046)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664877436356.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdvyInac-1681790306046)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664877447600.png)]
(2)批量插入:
UserMapper.xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ufCEIzHX-1681790306047)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664878336511.png)]UserMapper接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6crlrlWz-1681790306047)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664878379809.png)]
具体实现类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpe3OAuc-1681790306047)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664878397369.png)]
(3)sql片段:
我们在sql语句中应该避免使用 * 符号,将其改为sql片段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ov0ZfmNc-1681790306047)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664886082976.png)]
其他的相关有*符号的都替换掉:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uopMwRHY-1681790306047)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664886132053.png)]
9 . 多表查询:
已知数据库表:dept表emloyee表,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sTUCi8dZ-1681790306047)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947334753.png)]
dept表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dj4i0uol-1681790306048)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947361258.png)]
查询类似:
select * from employee e
LEFT JOIN dept d on d.id = e.did
主配置mybatis-config.xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lseQaNy-1681790306048)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947433665.png)]
dept类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-coZPJn8a-1681790306048)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947459306.png)]
employee类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EwPjfhVR-1681790306048)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947472246.png)]
对应的接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JyiSvjCc-1681790306048)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947498495.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0oiqujkd-1681790306048)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947513089.png)]
对应的xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-97vAgCk0-1681790306049)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947549954.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5V8UYje-1681790306049)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947565965.png)]
employ具体实现类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8otl56U4-1681790306049)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664947606177.png)]
10 . 高级映射: association:
(1)级联查询(子查询):
以下查询类似:
select * from employee e where e.did in (
select d.id from dept d
)
EmployeeMapper.xml:.select是代表的查询,意思是从Dept中查询,如果是修改的sql,则是.update
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BU8vA7xq-1681790306049)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664961456287.png)]
DeptMapper.xml:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msSn0eSC-1681790306049)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664955846618.png)]
(2)结果嵌套(类似left join)常用:
查询类似:
select e.id eid,e.name ename,d.id did,d.name dname
from employee e left join dept d on d.id = e.did
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWmiOJGi-1681790306049)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664960397797.png)]
相关接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rYnTEArM-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664960431530.png)]
具体实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XMVjUiVg-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664960453189.png)]
11 . 高级映射collection:
(1)级联查询:
查询思路sql语句:
select * from employee e where e.did in (
select d.id from dept d
)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMHDgZxN-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664962156076.png)]
然后从dept中查到对应id:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bq9jsiHd-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664962198994.png)]
(2)结果嵌套:
DeptMapper.xml:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QnYbjH6j-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664972819603.png)]
如果此时拿到员工的信息后,还有公司表(代表每个员工所属公司),那么我们可以再用asssociation或者collection映射一个公司表来查询到每个员工的公司的信息:
company类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XKqPMQmK-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664980259450.png)]
在Employee类中添加:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QbY4mDWQ-1681790306050)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664980298201.png)]
companyMapper接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EuUxsIic-1681790306051)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664980326019.png)]
companyMapper.xml文件配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q4MZbldL-1681790306051)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1664980377913.png)]
12 . 懒加载:
按需加载,我们需要什么的时候再去进行什么操作。而且先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
当我们在使用单表或多表查询时,有时候查询会查询比如部门id和部门name,但有时也可能只查询部门id不会查询部门name,那么我们就要再sql中进行修改,但是太麻烦了,所以我们使用懒加载方式,再需要时再从关联表中去关联查询,大大提高了数据库性能:当我们查某个列时,懒加载自动给我们关联到该信息,不用我们自己去sql语句更改。
配置信息:
<!-- 开启懒加载配置 -->
<settings>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
懒加载的底层是怎么实现的?
代理,方法的增强(比如对数据库的数据查询,懒加载后本来应该查询的值为null,但是它却能查到,用的动态代理)
13 . 获得自增的主键:
拿到id可以通过该id找相关联的表。
UserMapper.xml配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-afaawsGN-1681790306051)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665039528312.png)]
具体实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b7aL4020-1681790306051)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665039560735.png)]
已知表中数据userid最大为148,输出结果后,插入的user的id分明是null,可是我们看到它的id为149,这说明使用了自动递增主键
14 . (重点)Mybatis缓存:
(1)为什么要用缓存?
如果缓存中有数据,就不用从数据库获取,大大提高性能
mybatis提供一级缓存(默认使用)和二级缓存
- 在操作数据库时,需要构造sqlsession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据
- 不同的sqlsession之间的缓存区域是互相不影响的。
一级缓存一开始就会存在。
一级缓存:当前会话共享数据,与数据库连接时就会有一级缓存
二级缓存:不同绘画共享数据,mybatis的mapper执行使用的二级缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AflLcwJH-1681790306051)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665047105577.png)]
查询时没有该数据,所以通过sql从数据库查询,查询到的数据缓存到一级缓存,当重复查询此数据时,会先去一级缓存查询有没有此数据,有此数据直接用,没有再去数据库根据sql查询数据。
(2)一级缓存是sqlSession级别的缓存,
一级缓存失效的情况
1 .只能在相同的会话中,不然失效。
2 .当sqlSession对象相同的时候,查询的条件不同,原因是第一次查询时候,一级缓存中没有第二次查询所需要的数据
3 . 当sqlSession对象相同,两次查询之间进行了插入的操作(此时一级缓存失效是避免脏读)
4 . sqlSession对象相同,手动清除了一级缓存中的数据
(3)二级缓存:
不同的会话下能共享数据。
首先在主配置文件mybatis-config.xml下配置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZMt3BoT-1681790306052)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665050390886.png)]
随后在你要操作的Mapper中进行配置cache,如在UserMapper.xml中配置,你可以在该标签写上你的内容也可以不写:
如下:
<!--开启本Mapper的namespace下的二级缓存-->
<cache eviction="LRU" flushInterval="100000" size="512" readOnly="true"></cache>
<!--
创建了一个 LRU 最少使用清除缓存,每隔 100 秒刷新,最多可以存储 512 个对象,返回的对象是只读的。
-->
但我们的UserMapper.xml这里选择不写:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pzhjjg7x-1681790306052)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665050632636.png)]
这样我们的二级缓存就生效了。
测试二级缓存:
根据上述配置完成的信息,具体实现类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Usw0J4or-1681790306052)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665052585540.png)]
输出结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rosFD6wx-1681790306052)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665052630265.png)]
由此可知,它告诉了我们使用到了二级缓存,第一次查询通过sql语句查询到数据库获取数据,第二次新建会话查询相同数据时,它通过二级缓存查询到有数据,直接使用此数据。
注意:
数据库的数据一旦发生变化,缓存的数据必须清空,必须保证缓存与数据库的数据一致(避免脏读)
二级缓存一般情况很少开启,因为二级缓存可以共享其他会话的数据,那么当其中一个会话的数据与另一个会话的数据有相关联时,若修给了其中一个会话的数据,那么该会话的缓存也会清空,而另一个会话的缓存因为所在会话的数据没有修改缓存不会被清空,导致实际上该会话的数据已经和另一个会话数据不一致了。
禁用二级缓存:
在statement中可以设置useCache=false,禁用当前select语句的二级缓存,默认情况为true
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student" useCache="false">
1
在实际开发中,针对每次查询都需要最新的数据sql,要设置为useCache=“false” ,禁用二级缓存
flushCache标签:刷新缓存(清空缓存)
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student" flushCache="true">
1
一般下执行完commit操作都需要刷新缓存,flushCache="true 表示刷新缓存,可以避免脏读
(4)cache属性简介:
eviction回收策略(缓存满了的淘汰机制),目前MyBatis提供以下策略。
- LRU(Least Recently Used),最近最少使用的,最长时间不用的对象
- FIFO(First In First Out),先进先出,按对象进入缓存的顺序来移除他们
- SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象,当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有内存不足,JVM才会回收该对象。
- WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。弱引用的特点是不管内存是否足够,只要发生GC,都会被回收。
flushInterval:刷新间隔时间,单位为毫秒,
- 这里配置的是100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。
size:引用数目,
-
一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
**readOnly:**只读,
-
意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,它的默认值是false,不允许我们修改
格式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O6T4P52v-1681790306052)(C:\Users\CG\AppData\Roaming\Typora\typora-user-images\1665053634655.png)]
15 . 使用第三方缓存ehcache:
pom.xml中引入依赖:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
然后我们在对应的Mapper.xml使用这个缓存:
type固定缓存是使用ehcache缓存,创建了一个 LRU 最少使用清除缓存,每隔 100 秒刷新,最多可以存储 512 个对象,返回的对象是只读的。
<cache type="org.mybatis.caches.ehcache.EhcacheCache" eviction="LRU" flushInterval="10000" size="512" readOnly="true"></cache>
">
1
一般下执行完commit操作都需要刷新缓存,flushCache="true 表示刷新缓存,可以避免脏读
## (4)cache属性简介:
> **eviction**回收策略(缓存满了的淘汰机制),目前MyBatis提供以下策略。
1. LRU(Least Recently Used),最近最少使用的,最长时间不用的对象
2. FIFO(First In First Out),先进先出,按对象进入缓存的顺序来移除他们
3. SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象,当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有内存不足,JVM才会回收该对象。
4. WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。弱引用的特点是不管内存是否足够,只要发生GC,都会被回收。
> **flushInterval**:刷新间隔时间,单位为毫秒,
1. 这里配置的是100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。
> **size**:引用数目,
1. 一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
> **readOnly:**只读,
1. 意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,它的默认值是false,不允许我们修改
格式:
[外链图片转存中...(img-O6T4P52v-1681790306052)]
# 15 . 使用第三方缓存ehcache:
pom.xml中引入依赖:
```xml
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
然后我们在对应的Mapper.xml使用这个缓存:
type固定缓存是使用ehcache缓存,创建了一个 LRU 最少使用清除缓存,每隔 100 秒刷新,最多可以存储 512 个对象,返回的对象是只读的。
<cache type="org.mybatis.caches.ehcache.EhcacheCache" eviction="LRU" flushInterval="10000" size="512" readOnly="true"></cache>