一. 资料来源
mybatis B站视频链接
b站视频链接,点击跳转
Mybatis的官网
二. 大概分了如下模块:
狂神说Mybatis01:第一个程序
狂神说Mybatis02:CRUD操作及配置解析
狂神说Mybatis03:ResultMap及分页
狂神说Mybatis04:使用注解开发
狂神说Mybatis05:一对多和多对一处理
狂神说Mybatis06:动态SQL
狂神说Mybatis07:缓存
三.正式开始学习
(一)Mybatis学前理解
- 环境搭建
- jdk 8 +
- MySQL 5.7.19
- maven-3.6.1
- IDEA
学习前需要掌握: - JDBC
- MySQL
- Java 基础
- Maven
- Junit
- 什么是Mybatis
-
MyBatis 是一款优秀的持久层框架
-
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
-
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
-
MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
-
2013年11月迁移到Github .
-
Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
-
GitHub : https://github.com/mybatis/mybatis-3
- 持久化
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
-
即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
-
JDBC就是一种持久化机制。文件IO也是一种持久化机制。
-
在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。
为什么需要持久化服务呢?那是由于内存本身的缺陷引起的
-
内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
-
内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永
- 持久层
什么是持久层?
-
完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
-
大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
-
不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.
-
与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】
- 为什么需要持久层
-
Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .
-
传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .
-
MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
-
所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别
MyBatis的优点
-
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
-
灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
-
解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
-
提供xml标签,支持编写动态sql。
-
…
-
最重要的一点,使用的人多!公司需要!
(二)Mybatis第一个程序
1. 搭建试验环境
- 创建数据库和表等
create database `mybatis`;
use mybatis;
create table `user`(
`id` int(20) not null ,
`name` varchar(255) default NULL,
`pwd` varchar(255) default NULL,
primary key(id)
) engine=INNODB default charset=utf8;
//这个是主键:primary key(id)设置
//engine=INNODB default charset=utf8这个是设置引擎和编码的格式
insert into `user`(`id`,`name`,`pwd`) VALUES
(1,'狂神','1234'),
(2,'hello','1234'),
(3,'world','1234')
-
创建一个maven项目(普通的maven项目,不是以模板的形式创建)
1)创建maven项目:当做父工程
2)删除src
3)导入依赖
<?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>org.example</groupId>
<artifactId>helloMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 创建一个新模块:Mybatis-01
1)编写核心配置文件:配置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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.cj.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="111"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/mldn/mybatis/mapper/userMapper.xml"/>
</mappers>
</configuration>
2)创建mybatis核心配置文件
这里大概的介绍一下:以前我们是需要stament来进行读取SQL语句,
而Mybatis需要用SQLSession来进行读取:下面获取SqlSession对象(它是包含有读取SQL语句的命令)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//当然这类也就是死的,不会变化
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;
public class mybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步,获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
2.开始编写代码了
有了上面的配置之后,接下来我们就开始编写代码的了:一般有如下
- 实体类
- 操作数据库的类(DAO接口)
- DAO接口的实现
1)编写实体类
package cn.mldn.mybatis.bean;
public class user {
private int id;
private String name;
private String pwd;
@Override
public String toString() {
return "user{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
2)编写DAO和实现
记住编写DAO的实现的Mapper.xml的namespace属性的重要性哦,千万不能出错
文件UserMapper.java
package cn.mldn.mybatis.mapper;
import cn.mldn.mybatis.bean.user;
import java.util.List;
public interface UserMapper {
List<user> selectUser();
}
文件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="cn.mldn.mybatis.mapper.UserMapper">
<select id="selectUser" resultType="cn.mldn.mybatis.bean.user">
select * from user
</select>
</mapper>
这里我们来分析一下:我们为什么创建那个核心配置文件呢?创建它来有什么用呢?
a. 其实把它创建来就是读取userMapper和userMapper.xml文件的,
b. 这两个通过一个namespace相互连接起来
3) 可以测试了吧
package cn.mldn.mybatis.mapper;
import cn.mldn.mybatis.bean.user;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import cn.mldn.mybatis.utils.mybatisUtils;
import java.util.List;
public class UserMapperTest {
@Test
public void test() {
//第一步活的SqlSession
SqlSession session = mybatisUtils.getSession();
//执行sql
UserMapper mapper = session.getMapper(UserMapper.class);
//得到了这mapper就可以开始一系列的方法了
List<user> users = mapper.getUserList();
for (user user : users) {
System.out.println(user);
}
//关闭流
session.close();
}
}
如果报错就看我另外专门记载报错的文章吧:具体在Mybatis入门那里得问题
到这里第一节内容就结束了,记得Mybatis的内容一定要自己动起手来
(三)Mybatis的CRUD和配置解析
1. 试验环境搭建
这次就是昨天创建的父项目,新建一个子项目,就可以了
2. 具体的标签内容
(1). Select标签
- 标签介绍
- select标签是Mybatis中最常用的标签之一
- select语句有很多属性可以详细配置每一句SQL语句
- SQL语句的返回值类型:完整的类名或者别名(resultType)
- 传入SQL语句的参数类型:万能的Map,可以多尝试使用( parameterType)
- 命名空间中唯一的标识符
- 接口中的方法名和映射文件中的SQL语句ID–对应
- id
- parameterType
- resultType
- 编写代码测试
public interface UserMapper {
//查询全部用户
List<User> selectUser();
//根据id查询用户
User selectUserById(int id);
}
<select id="selectUserById" resultType="com.kuang.pojo.User">
select * from user where id = #{id}
</select>
@Test
public void tsetSelectUserById() {
SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
-
课堂练习:根据密码和名字查询用户
思路一:直接在方法中传递参数 1. 直接在方法中传递参数1、在接口方法的参数前加 @Param属性 2、Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型 3.代码: //通过密码和名字查询用户 User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd); /* <select id="selectUserByNP" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select> */ 思路2.使用万能的Map 1.在接口的方法中,参数直接传递Map User selectUserByNP2(Map<String,Object> map); 2. 编写Sql语句的时候,需要传递参数的类型,参数类型为Map <select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select> 3. 在使用方法的时候,Map的key为SQL中取的值即可,没有顺序要求! Map<String, Object> map = new HashMap<String, Object>(); map.put("username","小明"); map.put("pwd","123456"); User user = mapper.selectUserByNP2(map);
总结:总结:如果参数有两个,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可
(2)insert标签,这里一定要记得事务的问题哦
我们一般使用insert标签进行插入操作,它的配置和Select标签差不多
增加一个用户
-
在UserMapper接口中添加对应的方法
Integer addUser(user user); -
编写Sql语句
<update id="updateUser" parameterType="com.kuang.pojo.User"> update user set name=#{name},pwd=#{pwd} where id = #{id} </update>
-
测试
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
user.setPwd("asdfgh");
int i = mapper.updateUser(user);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
}
(3)delete标签
根据id删除一个用户
- 编写接口方法
Integer delete(Integer id; - 编写对应的配置文件SQL
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
- 测试
@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.deleteUser(5);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
}
这里来小结一下
-
所有的增删改操作都需要提交事务!
-
接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
-
有时候根据业务的需求,可以考虑使用map传递参数!
-
为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!
(4)update修改用户的信息
- 编写接口方法
//修改一个用户
int updateUser(User user); - 编写对应的配置文件SQL
<update id="updateUser" parameterType="com.kuang.pojo.User">
update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
- 测试
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
user.setPwd("asdfgh");
int i = mapper.updateUser(user);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
}
(5)like的配置
- 第一种:在在java代码中天街SQL通配符
string weldcardname = "%smi%"
List<name> names = mapper.selectLike(wildcardname)
<select id="selectLike">
select * from foo where bar like#{value}
</select>
- 第二种:在Sql语句拼接通配符,会引起SQL注入
这种有助于:在业务的时候防止注入
string wildcardname="smi"
List<name> names = mapper.selectlike(wildcardname)
<select id="selectlike">
select * from foo where bar like "%"#{value}"%"
</select>
(6)万能的map用法
- 在接口方法中,参数直接传递map
public interface userMapper {
user selectUser(Map<String, Object> map);
user selectUser1(String username);
}
- 然后再到我们的mapper文件中进行编写
<mapper namespace="cn.mldn.mybatis2.mapper.userMapper">
<select id="selectUser" parameterType="map" resultType="cn.mldn.mybatis2.bean.user">
select * from user where name = {#username} and pwd = {#pwd}
</select>
<select id="selectUser1" parameterType="String" resultType="cn.mldn.mybatis2.bean.user">
select * from user where name = {#username}
</select>
</mapper>
- 再来编写我们的测试类
@Test
public void test() {
//第一步活的SqlSession
SqlSession session = mybatisUtils.getSession();
//执行sql
userMapper mapper = session.getMapper(userMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("username","小明");
map.put("pwd","12345");
user user = mapper.selectUser(map);
System.out.println(user);
//关闭流
session.close();
}
毫无疑问测试报错,TMD手贱得很。
要牢牢的记住SQLSyntaxErrorException
:表示你的语法有问题:select * from user where name = {#username}
改为:select * from user where name = #{username} and pwd = {#pwd}
我们来对这个万能的map总结一波,在工作中,如果想那个SelectUser2一样,之后少数的参数,那我们直接编写用String 或者Integer等类型就可以了,但是如果我们的参数太多的情况下,那我们就用map是比较合适的
3. 接下来配置解析
mybatis-config.xml系统核心配置文件
mybatis的配置文件包含了会深深影响mybatis行为的设置和属性嘻嘻
- 能配置内容如下
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->
- 首先来看一下我们的environments元素
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
-
配置mybatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个默认的运行环境(通过上面的那个default确定)
-
子元素节点:Environment
- DataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象资源
- 数据源是必须配置的
- 有三种内建的数据源类型
type="[UNPOOLED|POOLED|JNDI]"
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED这种数据的实现利用“池”的概念将JDBC连接对象组织起来,这是一种使用并发web应用快速响应请求的流行处理,然后放置一个JNDI上下文的应用。同时还有很多其他的。
- 详细点击官方文档查看
- 这两种事务管理器类型都不需要设置任何属性
- 具体的一套环境,通过设置id进行区别,id保证唯一
- 子元素节点:transactionManager—【事务管理器】
transactionManager type="[JDBC|MANAGED]"
- 子元素节点:数据源DataSource
-
mappers
-
映射器:定义映射SQL语句文件
-
既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式允许以Java代码的方式注解定义SQL语句,非常简洁。
-
引入资源方式
-
使用想对应类路径的资源引用
<mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
-
第二种是完全限定资源定位符(URL)
<mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers>
-
配置文件名称和接口名称一致
<mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers>
-
映射器接口实现全部注册到映射器,但是需要配置文件名称和接口名称一致,并且位于同一目录下面
<mappers> <package name="org.mybatis.builder"/> </mappers>
-
- Mapper文件
<?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.kuang.mapper.UserMapper">
</mapper>
-
namespace中文意思:命名空间,作用如下:
- namespace的命名必须跟某一个接口同名
- 接口中的方法与映射文件中SQL语句中id应该一一对应1. namespace和子元素的id联合保证唯一,区别不同的mapper 2. 绑定DAO接口 3. namespace命名规则:保密+类名
总结:MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦
- Properties优化
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。具体的官方文档
我们来优化我们的配置文件
第一步 ; 在资源目录下新建一个db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456
第二步:将文件导入properties配置文件
<configuration>
<!--导入properties文件-->
<properties resource="db.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="mapper/UserMapper.xml"/>
</mappers>
</configuration>
- typeAliases优化
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<!--配置别名,注意顺序-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
这样就相当于:User可以用在任何使用com.kuang.pojo.User的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
若有注解,则别名为其注解值。见下面的例子:
@Alias("user")
public class User {
...
}
- 其他配置
设置的配置
-
设置(settings)相关—>查看帮助文档
- 懒加载
- 日志实现
- 缓存开启关闭
-
一个配置完整的settings元素的示例如下“”
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
- 类型处理器
-
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
-
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
- 对象工厂
- mybatis每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成
- 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么参数映射存在的时候通过有参构造方法实例化
- 如果要想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现(了解即可)
- 生命周期和作用域
-
作用域(Scope)和生命周期
理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
我们来看一个流程图,分析一下mybatis的执行流程
理解作用域 -
SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
-
SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
-
由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
-
因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
-
如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。
-
所以 SqlSession 的最佳的作用域是请求或方法作用域。
(四)再来看看ResultMap及分页查询
1. 查询为null的问题
-
问题引入
就是我们的实体类和我们的数据表里面的字段对不上的时候,那我们查询不出来相应的字段
就是password和pwd对不上,此时我能的密码栏会显示为null
User{id=1, name=‘狂神’, password=‘null’}
-
那我们要怎么解决这样的问题呢?
select * from user where id = #{id} 可以看做 select id,name,pwd from user where id = #{id} mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相 应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】
(1)第一种:改变列名
<select id="selectUserById" resultType="User">
select id , name , pwd as password from user where id = #{id}
</select>
(2)第二种,是我们用结果映射-->ResultMap【推荐】
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
2. ResultMap
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的JDBC
,ResultSets
数据提取代码中解放出来。- 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份
resultMap
能够代替实现同等功能的长达数千行的代码。 - 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。
- 自动映射
你已经见过简单映射语句的示例了,但并没有显式指定 resultMap。比如:
<select id="selectUserById" resultType="map">
select id , name , pwd
from user
where id = #{id}
</select>
上述语句只是简单地将所有的列映射到 HashMap
的键上,这由 resultType
属性指定。虽然在大部分情况下都够用,但是 HashMap
不是一个很好的模型。你的程序更可能会使用 JavaBean
或 POJO(Plain Old Java Objects,普通老式 Java 对象)
作为模型。
ResultMap
最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。
- 手动映射
(1)返回值类型为resultMap
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
(2)编写resultMap,实现手动映射
<resultMap id="UserMap" type="cn.mldn.mybatis2.bean.user">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解,今天你们需要把这些知识都消化掉才是最重要的!理解结果集映射的这个概念!
3. 分页的几种方式
(1)首先来了解一下:日志工厂
- 问题来源
思考:我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率?
如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。
对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
- 标准日志实现
指定Mybatis应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug
(2)Log4j
- 简介
-
Log4j是Apache的一个开源项目
-
通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…
-
我们也可以控制每一条日志的输出格式;
-
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- 使用步骤
(1)导入log4j包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
(2)配置文件编写
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
(3)settings设置日志实现,在我们的配置文件里面写< settings>< /settings>标签
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
(4)在程序中使用Log4j进行输出
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void selectUser() {
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
(5)测试,看控制台输出!
-
使用Log4j 输出日志
-
可以看到还生成了一个日志的文件 【需要修改file的日志级别】
(3)limit实现分页
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
-------第一种实现
-
使用Limit实现分页
#语法 SELECT * FROM table LIMIT stratIndex,pageSize SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 #为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. #如果只给定一个参数,它表示返回最大的记录行数目: SELECT * FROM table LIMIT 5; //检索前 5 个记录行 #换句话说,LIMIT n 等价于 LIMIT 0,n。
-
使用步骤
(1)修改Mapper文件
<select id="selectUser" parameterType="map" resultType="user">
select * from user limit #{startIndex},#{pageSize}
</select>
(2)Mapper接口,参数为map
//选择全部用户实现分页
List<User> selectUser(Map<String,Integer> map);
(3)传入参数测试
- 起始位置 = (当前页面 - 1 ) * 页面大小
//分页查询 , 两个参数startIndex , pageSize
@Test
public void testSelectUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int currentPage = 1; //第几页
int pageSize = 2; //每页显示几个
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("startIndex",(currentPage-1)*pageSize);
map.put("pageSize",pageSize);
List<User> users = mapper.selectUser(map);
for (User user: users){
System.out.println(user);
}
session.close();
}
-------第二种实现
我们除了使用Limit在SQL层面实现分页,也可以使用RowBounds在Java代码层面实现分页,当然此种方式作为了解即可。我们来看下如何实现的!
- RowBounds来完成
基于我们的代码成,上面中方式是基于我们的数据库里面的那种 - 步骤
(1)mapper接口
//选择全部用户RowBounds实现分页
List<User> getUserByRowBounds();
(2)mapper文件
<select id="getUserByRowBounds" resultType="user">
select * from user
</select>
(3)测试类
在这里,我们需要使用RowBounds类
@Test
public void testUserByRowBounds() {
SqlSession session = MybatisUtils.getSession();
int currentPage = 2; //第几页
int pageSize = 2; //每页显示几个
RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
//通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user: users){
System.out.println(user);
}
session.close();
}
------ 第三,其他方式
- PageHelper方式,可以自行了解!
(五)使用注解开发
1. 面向接口编程
-
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
-
根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
-
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
-
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
(1)关于接口的理解
-
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
-
接口的本身反映了系统设计人员对系统的抽象理解。
-
接口应有两类:
-
第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
-
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
-
-
一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
(2)三个面向区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构
(3)利用注解开发
- mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
- sql类型主要分成
- @select
- @update
- @Insert
- @delete
利用注解开发就是不需要使用mapper.xml文件
步骤:
- 在接口中方法上添加注解
//查询全部用户
@Select("select id,name,pwd password from user")
public List<User> getAllUser();
- 在mybatis的核心配置文件中注入
<!--使用class绑定接口-->
<mappers>
<mapper class="com.kuang.mapper.UserMapper"/>
</mappers>
- 进行测试
@Test
public void testGetAllUser() {
SqlSession session = MybatisUtils.getSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getAllUser();
for (User user : users){
System.out.println(user);
}
session.close();
}
- 利用Debug查看一下本质
- 本质上利用jvm的动态代理机制
- Mybatis详细的执行流程
(4)利用注解开发增删改
注解增删改
//获取SqlSession连接
public static SqlSession getSession(){
return getSession(true); //事务自动提交
}
public static SqlSession getSession(boolean flag){
return sqlSessionFactory.openSession(flag);
}
----查询
1、编写接口方法注解
//根据id查询用户
@Select("select * from user where id = #{id}")
User selectUserById(@Param("id") int id);
2、测试
@Test
public void testSelectUserById() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
----添加
1,编写接口方法注解
//添加一个用户
@Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
int addUser(User user);
2、测试
@Test
public void testAddUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(6, "秦疆", "123456");
mapper.addUser(user);
session.close();
}
----修改
1、编写接口方法注解
//修改一个用户
@Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
int updateUser(User user);
2、测试
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(6, "秦疆", "zxcvbn");
mapper.updateUser(user);
session.close();
}
----删除
1、编写接口方法注解
//根据id删除用
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id")int id);
2、测试
@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser(6);
session.close();
}
【注意点:增删改一定记得对事务的处理】
(5)使用@Param
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
-
在方法只接受一个参数的情况下,可以不使用@Param。
-
在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
-
如果参数是 JavaBean , 则不能使用@Param。
-
不使用@Param注解时,参数只能有一个,并且是Javabean。
(6)#与$的区别
- #{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
- ${} 的作用是直接进行字符串替换
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');
使用注解和配置文件协同开发,才是MyBatis的最佳实践!
(六)一对多和多对一处理
1. 多对一的处理
(1). 多对一的理解:
-
多个学生对应一个老师
-
如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!
(2) 准备测试环境
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
看这个是我们的外键关联的时候用的
(3). 使用Lombok
(1)idea安装Lombok插件
(2)引入Maven依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
(4)编写代码
- 数据库对应类
@Data //GET,SET,ToString,有参,无参构造
public class Teacher {
private int id;
private String name;
}
public class Student {
private int id;
private String name;
//多个学生可以是同一个老师,即多对一
private Teacher teacher;
}
- 编写实体类对应的Mapper接口 【两个】
- 无论有没有需求,都应该写上,以备后来之需
public interface StudentMapper {
}
public interface TeacherMapper {
}
- 编写Mapper接口对应的mapper.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="cn.mldn.mybatis03.mapper.StudentMapper">
</mapper>
<?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="cn.mldn.mybatis03.mapper.TeacherMapper">
</mapper>
----第一种方式:按结果嵌套处理
-
查询嵌套处理
(1)StudentMapper接口增加方法
//获取所有学生及对应老师的信息
public List<Student> getStudents();
(2)编写对应的Mapper文件
<?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="cn.mldn.mybatis03.mapper.StudentMapper">
<!--
需求:获取所有学生及对应老师的信息
思路:
1. 获取所有学生的信息
2. 根据获取的学生信息的老师ID->获取该老师的信息
3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
1. 做一个结果集映射:StudentTeacher
2. StudentTeacher结果集的类型为 Student
3. 学生中老师的属性为teacher,对应数据库中为tid。
多个 [1,...)学生关联一个老师=> 一对一,一对多
4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
-->
<select id="getStudents" resultMap="StudentTeacher" resultType="cn.mldn.mybatis03.bean.Student">
select * from student
</select>
<resultMap id="StudentTeacher" type="cn.mldn.mybatis03.bean.Student">
<association property="teacher" column="tid" javaType="cn.mldn.mybatis03.bean.Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="cn.mldn.mybatis03.bean.Teacher">
select * from teacher where id=#{id}
</select>
</mapper>
这下面有点注意点哦
<resultMap id="StudentTeacher" type="Student">
<!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
<association property="teacher" column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!--
这里传递过来的id,只有一个属性的时候,下面可以写任何值
association中column多参数配置:
column="{key=value,key=value}"
其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。
-->
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id} and name = #{name}
</select>
(3)在Mybatis配置文件中,注册Mapper
<mappers>
<!--<mapper resource="cn/mldn/mybatis03/mapper/userMapper.xml"/>-->
<mapper resource="cn/mldn/mybatis03/mapper/StudentMapper.xml"/>
<mapper resource="cn/mldn/mybatis03/mapper/TeacherMapper.xml"/>
</mappers>
(4)测试
@Test
public void testGetStudents(){
SqlSession session = MybatisUtils.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents();
for (Student student : students){
System.out.println(
"学生名:"+ student.getName()
+"\t老师:"+student.getTeacher().getName());
}
}
----第二种方式:按查询嵌套处理
除了上面这种方式,还有其他思路吗?
我们还可以按照结果进行嵌套处理;
-
按照结果嵌套处理
(1)StudentMapper接口中写方法
public List<Student> getStudents2();
(2)然后再编写我们的Mapper文件
<!--
按查询结果嵌套处理
思路:
1. 直接查询出结果,进行结果集的映射
-->
<select id="getStudents2" resultMap="StudentTeacher2" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
(3)去mybatis-config文件注入
(4)测试
@Test
public void testGetStudents2(){
SqlSession session = MybatisUtils.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents2();
for (Student student : students){
System.out.println(
"学生名:"+ student.getName()
+"\t老师:"+student.getTeacher().getName());
}
}
----这里我们统计一下常会出错的地方
"C:\Program Files\Java\jdk1.8.0_241\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\use\IntelliJ IDEA 2020.1.4\lib\idea_rt.jar=60569:D:\use\IntelliJ IDEA 2020.1.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\use\IntelliJ IDEA 2020.1.4\lib\idea_rt.jar;D:\use\IntelliJ IDEA 2020.1.4\plugins\junit\lib\junit5-rt.jar;D:\use\IntelliJ IDEA 2020.1.4\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_241\jre\lib\rt.jar;D:\SpringBoot\helloMybatis\mybatis-03\target\test-classes;D:\SpringBoot\helloMybatis\mybatis-03\target\classes;C:\Users\郑广广\.m2\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar;C:\Users\郑广广\.m2\repository\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar;C:\Users\郑广广\.m2\repository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;C:\Users\郑广广\.m2\repository\org\mybatis\mybatis\3.5.7\mybatis-3.5.7.jar;C:\Users\郑广广\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\郑广广\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 cn.mldn.mybatis03.hello3,testGetStudents2
java.lang.ExceptionInInitializerError
at cn.mldn.mybatis03.hello3.testGetStudents2(hello3.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in cn/mldn/mybatis03/mapper/StudentMapper.xml
### The error occurred while processing mapper_resultMap[StudentTeacher2]_association[teacher]
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'cn/mldn/mybatis03/mapper/StudentMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'c.mldn.mybatis03.bean.Teacher'. Cause: java.lang.ClassNotFoundException: Cannot find class: c.mldn.mybatis03.bean.Teacher
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at cn.mldn.mybatis03.utils.mybatisUtils.<clinit>(mybatisUtils.java:20)
... 23 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'cn/mldn/mybatis03/mapper/StudentMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'c.mldn.mybatis03.bean.Teacher'. Cause: java.lang.ClassNotFoundException: Cannot find class: c.mldn.mybatis03.bean.Teacher
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:122)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:99)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
... 25 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'cn/mldn/mybatis03/mapper/StudentMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'c.mldn.mybatis03.bean.Teacher'. Cause: java.lang.ClassNotFoundException: Cannot find class: c.mldn.mybatis03.bean.Teacher
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:123)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:95)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:378)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)
... 27 more
Caused by: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'c.mldn.mybatis03.bean.Teacher'. Cause: java.lang.ClassNotFoundException: Cannot find class: c.mldn.mybatis03.bean.Teacher
at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:118)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:263)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.processNestedResultMappings(XMLMapperBuilder.java:400)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.lambda$buildResultMappingFromContext$0(XMLMapperBuilder.java:383)
at org.apache.ibatis.parsing.XNode.getStringAttribute(XNode.java:200)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildResultMappingFromContext(XMLMapperBuilder.java:382)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:280)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:254)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElements(XMLMapperBuilder.java:246)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:119)
... 30 more
Caused by: org.apache.ibatis.type.TypeException: Could not resolve type alias 'c.mldn.mybatis03.bean.Teacher'. Cause: java.lang.ClassNotFoundException: Cannot find class: c.mldn.mybatis03.bean.Teacher
at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:120)
at org.apache.ibatis.builder.BaseBuilder.resolveAlias(BaseBuilder.java:149)
at org.apache.ibatis.builder.BaseBuilder.resolveClass(BaseBuilder.java:116)
... 39 more
Caused by: java.lang.ClassNotFoundException: Cannot find class: c.mldn.mybatis03.bean.Teacher
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:196)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:89)
at org.apache.ibatis.io.Resources.classForName(Resources.java:261)
at org.apache.ibatis.type.TypeAliasRegistry.resolveAlias(TypeAliasRegistry.java:116)
... 41 more
有上面的报错的情况的话,那我们就去找我们的XXXMapper文件里面的是不是路劲呀,还是什么有错误,一般情况下是会是我们的路径有问题。
**总结
按照查询进行嵌套处理就像SQL中的子查询
按照结果进行嵌套处理就像SQL中的联表查询
我个人的话,比较喜欢第二种方式,但是两种都还是了解一下,比较好
**
2. 一对多处理
(1)一对多的理解:
-
一个老师拥有多个学生
-
如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!
下面可以借鉴一下我们的一对多的表创建,可以借鉴一下
DROP TABLE IF EXISTS `people`;
CREATE TABLE `people` (
`people_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`people_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
CREATE TABLE `phone` (
`phone_id` int(11) NOT NULL AUTO_INCREMENT,
`brand` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`people_id` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`phone_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
(2)编写实体类
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
//一个老师多个学生
private List<Student> students;
}
(3)编写代码
----第一种方式:按结果嵌套处理
- TeacherMapper接口编写方法
//获取指定老师,及老师下的所有学生
public Teacher getTeacher(int id);
- 编写接口对应的Mapper配置文件
<mapper namespace="com.kuang.mapper.TeacherMapper">
<!--
思路:
1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
2. 对查询出来的操作做结果集映射
1. 集合的话,使用collection!
JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型
ofType指定的是映射到list集合属性中pojo的类型。
-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname , t.name tname, t.id tid
from student s,teacher t
where s.tid = t.id and t.id=#{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
</mapper>
- 将Mapper文件注册到Mybatis-config文件里面‘
<mappers>
<mapper resource="mapper/TeacherMapper.xml"/>
</mappers>
- 测试
@Test
public void testGetTeacher(){
SqlSession session = MybatisUtils.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher.getName());
System.out.println(teacher.getStudents());
}
----第二种方式:按查询嵌套处理
- TeacherMapper接口编写方法
public Teacher getTeacher2(int id);
- 编写接口对应的Mapper配置文件
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{id}
</select>
-
将Mapper文件注册到MyBatis-config文件中
-
测试
@Test
public void testGetTeacher2(){
SqlSession session = MybatisUtils.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher.getName());
System.out.println(teacher.getStudents());
}
3. 小结
1、关联-association
2、集合-collection
3、所以association是用于一对一和多对一,而
collection是用于一对多的关系
4、JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型
ofType指定的是映射到list集合属性中pojo的类型。
注意说明:
1、保证SQL的可读性,尽量通俗易懂
2、根据实际要求,尽量编写性能更高的SQL语句
3、注意属性名和字段不一致的问题
4、注意一对多和多对一 中:字段和属性对应的问题
5、尽量使用Log4j,通过日志来查看自己的错误
(七)动态SQL编写
什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.
官网描述:
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
-------------------------------
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
-------------------------------
我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。
那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
1. 编写我们的实验环境
- 创建我们的表
CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
创建我们的Mybatis基础工程
我是从新创建一个的
-
创建我们的工具类IDUtil类
获取我们的永不重复的Id
public class IDUtil {
public static String genId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
- 编写我们的实体类
package com.kuang.pojo;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
@Data
@ToString
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
//set,get....
}
- 编写Mapper文件接口及Xml文件
public interface BlogMapper {
}
<?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.kuang.mapper.BlogMapper">
</mapper>
- 再编写我们的核心配置文件
如果有报错的话,那么请把你的Settings标签放到最开始
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--注册Mapper.xml-->
<mappers>
<mapper resource="mapper/BlogMapper.xml"/>
</mappers>
另外看这个:是我们的日志输出的,这个以后在我们的SpringBoot项目也要好好地理解
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 插入我们的数据
//新增一个博客
int addBlog(Blog blog);
<insert id="addBlog" parameterType="blog">
insert into blog (id, title, author, create_time, views)
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
初始化一下
@Test
public void addInitBlog(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtil.genId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("Java如此简单");
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("Spring如此简单");
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("微服务如此简单");
mapper.addBlog(blog);
session.close();
}
反正在这一步我的是怎么都不行,然后我在数据库里面自行插入的
到这里就搭建好了试验环境
2. if语句
1、编写接口类
//需求1
List queryBlogIf(Map map);
2、编写Sql文件
<!--需求1:
根据作者名字和博客名字来查询博客!
如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
select * from blog where title = #{title} and author = #{author}
-->
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog where
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
3 、测试一波
@Test
public void testQueryBlogIf(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","Mybatis如此简单");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
System.out.println(blogs);
session.close();
}
有问题的哦:
这样写我们可以看到,如果 author 等于 null,那么查询语句为 select * from user where title=#{title},但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!
2. Where测试
1、如果象上面出错情况编写
<select id="queryBlogIf" parameterType="map" resultType="com.kuang.pojo.Blog">
select * from blog where
<if test="title = null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
报错
rg.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and author = '狂神说'' at line 4
### The error may exist in mapper/BlogMapper.xml
### The error may involve com.kuang.mapper.BlogMapper.queryBlogIf-Inline
### The error occurred while setting parameters
### SQL: select * from blog where and author = ?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that c
2、修改我们的SQL语句
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
总结:
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
3. Set测试
同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?
1、 编写我们的接口方法
int updateBlog(Map map);
2、sql配置文件
<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id};
</update>
3、测试
@Test
public void testUpdateBlog(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","动态SQL");
map.put("author","秦疆");
map.put("id","9d6a763f5e1347cebda43e2a32687a77");
mapper.updateBlog(map);
session.close();
}
4. choose语句
1、功能介绍
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
2、 编写测试接口方法
List<Blog> queryBlogChoose(Map map);
3、SQL文件编写
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from 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>
4、测试
@Test
public void testQueryBlogChoose(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title","Java如此简单");
map.put("author","狂神说");
map.put("views",9999);
List<Blog> blogs = mapper.queryBlogChoose(map);
System.out.println(blogs);
session.close();
}
5. SQL片段
1、目的
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
2、提前SQL片段
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
3、引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
注意:
①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
6. Foreach介绍
1、 需求
将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
2、编写接口方法
List<Blog> queryBlogForeach(Map map);
3、编写SQL语句
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from blog where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
4、测试
@Test
public void testQueryBlogForeach(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
System.out.println(blogs);
session.close();
}
小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。
(八)缓存
1. 简介
1、什么是缓存 [ Cache ]?
-
存在内存中的临时数据。
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2、为什么使用缓存
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
3. 什么样的数据能使用缓存
- 经常查询并且不经常改变的数据。
2. Mybatis缓存
-
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
-
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
3. 一级缓存
一级缓存也叫本地缓存:
-
与数据库同一次会话期间查询到的数据会放在本地缓存中。
-
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;