目录
第一章:MyBatis概述
1.1 框架
1.2 三层架构
1.3 JDBC不足
1.4 了解MyBatis
第二章:MyBatis入门程序
2.1 开发第一个MyBatis程序:
2.2 关于Mybatis的事务管理机制深度剖析:
2.3 一个较完整的mybatis程序
2.4 Mybatis集成Junit使用
2.5 Mybatis如何集成日志?
2.6 MyBatis工具类的编写
第三章:使用MyBatis完成CRUD
3.1 如何给mapper.xml的sql语句动态传参
map集合方式
通过POJO对象传值
3.2 删除数据
3.3 修改数据
3.4 查询数据
查询一条记录
查询多条记录
3.5 SQL Mapper映射文件中的namespace作用
第四章:MyBatis核心配置文件详情
第五章:手写MyBatis框架(大致了解掌握原理)
5.1 dom4j解析xml文件
5.2 引入相关依赖
5.3 读取mybatis-config.xml配置文件
5.4 读取CarMapper.xml文件
5.5 创建基础类
第六章:在WEB中应用MyBatis
6.1 创建java web项目,引入依赖,配置Tomcat启动、核心xml文件,映射xml文件。
6.2 整体结构
6.3 前后端代码实现
6.3.1 前端
6.3.2 后端
6.4 WEB应用下mybatis的事务控制
6.5 mybatis三大对象的作用域和生命周期
第七章:MyBatis中接口代理对象使用
7.1 xml映射文件:需要满足一定的规则
7.2 如何获取到代理对象
7.3 使用
第八章:MyBatis小技巧
8.1 #{}和${}的区别
8.2 查找包含某个关键词str的方法
8.3 typeAliases 别名
8.5 IDEA配置文件模板
8.6 插入数据的时候获取当时插入的数据的主键
第九章:MyBatis参数处理
9.1 单个简单类型参数
9.2 参数为Map类型或者POJO类
9.3 多个参数的情况
9.4 @Param源码分析
第十章:Mybatis查询语句专题
10.1 返回结果集封装成实体类
10.2 返回结果集封装成多个实体类
10.3返回结果没有合适的实体类,这时候我们可以指定Map集合接收
10.4 结果集映射(指定使用结果集映射,将数据库字段名和Java类属性一一对应)
10.5 开启驼峰命名自动映射
第十一章:动态SQL
11.1 if 标签
11.2 where 标签
11.3 trim 标签
11.4 set标签
11.5 choose标签
11.6 foreach 标签
11.7 Sql标签与include标签
第十二章:Mybatis高级映射以及延迟加载
12.1 多对一高级映射的三种方法:
第一种:级联属性映射
第二种:采用association标签
第三种:分步查询(这种方法常用,优点一可复用,优点二支持懒加载)
12.2 一对多映射高级映射的两种种方法:
第一种方式(连表查询用collection封装)
第二种方式(分布查询)
第十三章:Mybatis缓存
13.1 一级缓存
13.2 二级缓存
一级缓存未关闭和提交情况下
一级缓存关闭和提交情况下,(先找一级缓存,再找二级缓存)
⼆级缓存的失效:只要两次查询之间出现了增删改操作。⼆级缓存就会失效。【⼀级缓存也会失效】
13.3 集成Ehcache
第十四章:逆向工程
14.1 代码生成
14.2QBC(Query By Criteria)查询风格。
第十五章:Pagehelper
15.1 limit分页
15.2 PageHelper插件
第十六章:注解式开发
16.1 @Insert @Delete @Update @Select注解
16.2 @Results注解
*表现层:直接跟前端打交互(一是接收前端ajax请求,二是返回json数据给前端)
*业务逻辑层:一是处理表现层转发过来的前端请求(也就是具体业务),二是将从持久层获取的数据返回到表现层。
*数据访问层:直接操作数据库完成CRUD,并将获得到的数据返回给上一层(也就是业务逻辑层)。
java持久层框架:MyBatis、Hibernate、JOOQ、Guzz、Spring Data、AvticeJDBC......等
1、sql语句写死在java程序中
2、JDBC代码比较繁琐,给sql语句填入值需要重复的set。
3、获取结果集需要遍历结果集,重复的new对象,重复的get数据库返回的数据,再重复的set数据给对象。
1、MyBatis是对jdbc的封装,通过MyBatis完成CRUD
2、MyBatis之前叫做ibatis,后来改名MyBatis
3、MyBatis是持久层框架
mybatis中文文档地址:https://mybatis.net.cn
*ORM:对象关系映射
O(Object):Java虚拟机中的Java对象
R(Relational):关系型数据库
M(Mapping):将Java虚拟机中的Java对象映射到数据库表中一行记录,或是将数据库中一行记录映射成Java虚拟机中的Java对象。
*创建数据表:并且插入测试数据
1、resources目录:放在这个目录当中的,一般都是资源文件,配置文件。直接放在目录下的资源,等同于放在类的根路径下。
2、开发步骤:
*第一步:打包方式 jar
*第二步:引入依赖
- mybatis依赖
- mysql驱动依赖
*第三步:编写mybatis核心配置文件,mybatis-config.xml
注意:这个文件名不是固定的,可以使用其他名字,只是大部分人都使用这个名字。
这个文件存放的位置也不是固定的,可以随意,但是一般情况下,会放到类的根路径下。
*第四步:编写XxxxMapper.xml文件,在这个配置文件当中编写SQL语句,这个文件名不是固定的,放的位置也不是固定的,叫做CarMapper.xml,暂时放在reources目录下。
*第五步:在mybatis-config.xml文件中指定XxxxMapper.xml文件所在的路径。
*第六步:编写Mybatis程序,使用mybatis的类库,编写mybatis程序,连接数据库,做CRUD操作即可。
在Mybatis中,负责执行SQL语句的对象叫做什么呢?:SqlSession。
SqlSession是专门用来执行SQL语句的,是一个java程序和数据库之间的一次会话。
如何获取SqlSessionFactory对象呢?首先获取SqlSessionFactoryBuilder对象,通过这个对象的build()方法,来获取一个SqlSessionFactory对象。
依次的获取顺序:SqlSessionFactoryBuilder对象 > SqlSessionFactory对象 > SqlSession对象
3、在XML中构建 SqlSessionFactory
在Mybatis中有一个很重要的对象,这个对象就是:SqlSessionFactory对象,创建这个对象需要Xml文件。
XML是什么? 它是一个配置文件。
4、Mybatis中有两个重要的配置文件:
其中一个是:mybatis-config.xml,这是核心配置文件,主要配置连接数据库的信息等。(一个)
另外一个是:XxxxMapper.xml,这个文件是专门用来编写SQL语句的配置文件(一个表对应一个配置文件)。
* 在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理
* type属性的值包括两个:
JDBC
MANAGED
type后面的值,只有以上两个值可选,不区分大小写。
* 在mybatis中提供了两种事务管理机制:
第一种:JDBC事务管理器
第二种:Managed事务管理器
* JDBC事务管理:
mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务
conn.setAutoCommit(false); 关闭自动提交,表示开启事务
.....业务处理...
conn.commit(); 手动提交事务
* MANAGED事务管理器:
mybatis不再负责事务的管理,事务管理交给其他容器来负责,例如:spring。
对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED,那么事务这块是没人管的,没有其他框架管理事务的话表示事务压根没有开启。
1、引入依赖
junit
junit
4.13
test
2、创建单元测试类,在测试方法上添加@Test注解,表示是一个单元测试方法
mybatis核心配置文件中可以开启运行时行为,开启日志,通过settings标签
* mybatis常见的集成的日志组件有哪些呢?
SLF4J(沙拉风)
LOG4J
LOG4J2
STDOUT_LOGGING
......
* 其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志,mybatis框架本身已经实现了这种标准。只要开启即可,怎么开启呢?在mybatis-config.xml文件中使用标签进行配置开启。
具体还可以设置哪些配置,参考:配置_MyBatis中文网
* 集成logback日志框架
logback日志框架实现了slf4j标准。(沙拉风:日志门面。日志标准)
引入这个可以不写mybatis-config.xml里的settings设置。
第一步:引入logback的依赖
ch.qos.logback
logback-classic
1.2.11
第二步:引入logback所必须的xml配置文件(用于定义日志的级别和输出的日志格式),配置文件的名字,必须叫logback.xml或者logback-test.xml,不能是其他的名字。这个配置文件必须放在类的根路径下。
[%thread] %-5level %logger{50} - %msg%n
public class SqlSessionUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
try {
sqlSessionFactory = factoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//构造方法私有化
private SqlSessionUtil() {
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
解决方法:传参,占位符获取参数。
//获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//将数据进行封装,这里封装成map集合
Map map = new HashMap<>();
map.put("arg1","10086");
map.put("arg2","雷克萨斯");
map.put("arg3",100.0);
map.put("arg4","2022-11-20");
map.put("arg5","燃油车");
//选择执行操作的方法,第一个参数指定id即执行的sql语句,第二个参数,sql语句的参数
int count = sqlSession.insert("insertCar",map);
System.out.println(count);
sqlSession.commit();
xml文件:
//获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//将数据进行封装,这里封装成POJO对象
Car car = new Car("10087","凯迪拉克",new BigDecimal(30),"2022-11-20","燃油车");
//选择执行操作的方法,第一个参数指定id即执行的sql语句,第二个参数,sql语句的参数
int count = sqlSession.insert("insertCar",car);
System.out.println(count);
sqlSession.commit();
xml文件:(通过属性的Get方法取值)
//获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//选择执行操作的方法,第一个参数指定id即执行的sql语句,第二个参数,sql语句的参数
int count = sqlSession.delete("deleteCarById",2);
System.out.println(count);
sqlSession.commit();
xml文件:(当只有一个参数的时候,#{}里面的名字可以随便写)
//获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//将需要数据的数据进行封装,这里封装成POJO对象
Car car = new Car(5,"10087","凯迪拉克",new BigDecimal(50),"2022-11-20","电车");
//选择执行操作的方法,第一个参数指定id即执行的sql语句,第二个参数,sql语句的参数
int count = sqlSession.update("updateCarById",car);
System.out.println(count);
sqlSession.commit();
xml文件:
需要特别注意的是,select标签中的resultType属性,用来告诉mybatis将查询的结果集封装成什么类型的java对象。
resultType属性的值通常写的是全限定类名
//获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//mybatis底层执行了select语句之后,一定会返回一个结果集对象,ResultSet
//JDBC中叫做ResultSet,接下来就是mybatis应该从ResultSet中拿数据,封装成POJO对象
Object car = sqlSession.selectOne("selectCarById", 7);
System.out.println(car);
xml文件:
结果:
发现了问题,为什么只有id和brand有数据呢?其他没有赋上值呢?
细心的会发现,没有值的属性都有一个共同点,就是的属性名字和数据库的字段名不一致,所以导致赋不上值。
解决方法:(现在这种方法先知道,后续有学到xml文件的标签使用的时候有更简单的方法,但是原理都是一样的)。
修改select语句,将查询的到的结果的字段名,使用as 来修改字段名,改成和java对象的属性名一致。
//获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//mybatis底层执行了select语句之后,一定会返回一个结果集对象,ResultSet
//JDBC中叫做ResultSet,接下来就是mybatis应该从ResultSet中拿数据,封装成POJO对象
//和其他方法类似,有参数的话就调用两个参数的方法
List
xml文件:
结果:
在 sql mapper.xml文件中有一个namespace,这个属性是用来指定命名空间的,用来防止id重复
Mapper映射文件中是可以有多个的,在多个Mapper映射文件中,如果有相同的id,就会出问题,不知道执行哪一个了。
命名空间是来防止id的冲突的,所以我们可以把 namespace来当作区别Mapper映射文件的标识。
因此我们在操作sqlSession去调用sql语句的时候,也可以加入namespace
引入依赖
org.dom4j
dom4j
2.1.1
简单使用
//创建SAXReader对象
SAXReader reader = new SAXReader();
//获取输入流
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("xml文件路径");
//读xml文件,返回document对象,document对象是文档对象,代表了整个xml文件
Document document = reader.read(is);
//获取文档对象的根标签
Element rootElement = document.getRootElement();
//获取根节点的名字
rootElement.getName();
//xpath是做标签路径匹配的,能给让我们快手定位XML文件中的元素。
//以下xpath代表了,从根下开始找configuration标签,然后找configuration标签下的子标签environments
String xpath = "/configuration/environments";
//原来是Node类,强转成Element,Element是Node类的子类,方法更多
Element environments = (Element)document.selectSingleNode(xpath);
//获取属性的值
environments.attributeValue("标签内的属性名");
//当configuration标签下的environments标签有两个相同的子标签environment,如何获取指定的那个呢?
String xpath2 = "/configuration/environments/environment[@属性名='"+属性值+"']";
Element environment = (Element)document.selectSingleNode(xpath2);
//获取某标签下的指定子标签
Element subElement = environment.element("标签名");
//获取标签来的所有子标签
List elements = environment.elements();
//不想从根下开始,去获取所有的某个标签
String xpath3 = "//标签名";
List nodes = document.selectNodes(xpath3);
org.dom4j
dom4j
2.1.3
jaxen
jaxen
1.2.0
junit
junit
4.13.2
test
@Test
public void ParseMyBatisConfigXml() throws Exception {
//创建SAXReader对象
SAXReader reader = new SAXReader();
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
//xml对象
Document document = reader.read(in);
String xPath = "/configuration/environments";
Element environments = (Element) document.selectSingleNode(xPath);
String default = environments.attributeValue("default");
xPath = "/configuration/environments/environment[@id='"+default +"']";
Element environment = (Element) document.selectSingleNode(xPath);
Element transactionManager = environment.element("transactionManager");
//事务类型
String transactionType = transactionManager.attributeValue("type");
List dataSources = environment.element("dataSource").elements();
//打印连接源
dataSources.forEach(properties ->{
String name = properties.attributeValue("name");
String value = properties.attributeValue("value");
// TODO 提取数据出来封装
});
//mapp映射文件路径
xPath = "//mapper";
List elements = document.selectNodes(xPath);
elements.forEach(element -> {
Element mapper = (Element) element;
String resource = mapper.attributeValue("resource");
// TODO 提取数据出来封装
});
}
/**
* 解析CarMapper.xml文件
*/
@Test
public void ParseSqlConfigXml() throws Exception {
SAXReader reader = new SAXReader();
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("CarMapper.xml");
Document document = reader.read(in);
Element mapper = document.getRootElement();
System.out.println(mapper.attributeValue("namespace"));
List elements = mapper.elements();
elements.forEach(element -> {
System.out.println(element.attributeValue("id"));
String sql = element.getTextTrim();
//简单正则表达式:[a-zA-Z0-9_$]* 表示数字+字母+下划线,数量是*个
System.out.println(sql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?"));
});
}
//编写Resources类
public class Resources {
private Resources(){};
public static InputStream getResourceAsReader(String classPath){
return ClassLoader.getSystemClassLoader().getResourceAsStream(classPath);
}
}
//创建SqlSessionFactoryBuild类,其中需要SqlSessionFactory类对象
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder(){};
public SqlSessionFactory build(){
return null;
}
}
public class SqlSessionFactory {
/**
* 事务管理器
*/
private Transaction transaction;
/**
* 存放sql语句的map集合
* key是sqlId
* value是对应的sql标签信息对象
*/
Map mappedStatements;
}
public class JdbcTransaction implements Transaction{
@Override
public void commit() {
connection.commit();
}
@Override
public void rollback() {
connection.rollback();
}
@Override
public void close() {
connection.close();
}
}
/**
* @author huochai
* @date 2022/10/15 16:45
* 不使用连接池,主要实现这个数据源
*/
public class UnPooledDataSource implements DataSource {
private String url;
private String username ;
private String password ;
public UnPooledDataSource(String url, String username, String password) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
this.url = url;
this.username = username;
this.password = password;
}
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public T unwrap(Class iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
return false;
}
}
目标:
* 掌握mybatis在web应用中怎么去使用
* mybatis三大对象的作用域和生命周期
* ThreadLocal原理以及使用
* 巩固MVC架构模式
* 为学习Mybatis的接口代理机制做准备
org.mybatis
mybatis
3.5.9
mysql
mysql-connector-java
8.0.30
javax.servlet
javax.servlet-api
4.0.1
ch.qos.logback
logback-classic
1.2.11
web.xml文件
使用方式和普通java程序类似,只不过Java Web项目按照三层架构来开发。
创建SqlSessionFactoryBuild -》 SqlSessionFactory -》 SqlSession
创建Servlet处理请求。接收参数,表示层调用业务层,业务层调持久层,持久层使用mybatis操作数据库数据。
实体类
public class Account {
private Long id;
private String actno;
private Double balance;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
}
表示层
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
//为了让这个对象在其他方法中也可以调用,声明为实例遍历
private AccountService accountService = new AccountServiceImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取表单数据
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
Double money = Double.valueOf(request.getParameter("money"));
//表示层不处理业务逻辑的,而是调用service(业务层)的转账方法完成转账,
accountService.transfer(fromActno,toActno,money);
//调用View完成展示结果
}
}
业务层
接口
/**
* 面向接口,创建一个接口 账户业务类
* 业务类当中的业务方法的名字在起名的时候,最好见名知意,能够体现出具体的业务是做什么的
*/
public interface AccountService {
/**
* 账户转账业务
* @param fromActno 转出账户
* @param toActno 转入账户
* @param money 金额
*/
void transfer(String fromActno,String toActno,double money);
/**
* 查询用户
* @param id
* @return
*/
Account selectById(Long id);
}
实现类
public class AccountServiceImpl implements AccountService { @Override public void transfer(String fromActno, String toActno, double money) { //这个类负责处理业务 //判断转出账户的余额是否充足(查询) //转出账户余额不足 //转出账户余额充足,更新转出账户余额 //更新转入账户余额 } @Override public Account selectById(Long id) { //根据id查询用户 return null; } }
持久层
接口
/**
* 账户Mapper(Dao)对象,负责对数据库的数据进行操作
* Mapper(Dao)对象中的任何一个方法和业务都不挂钩,没有业务逻辑在里面,
* Dao中的方法就是CRUD,不含业务逻辑代码,对数据库的操作不会被业务逻辑代码影响,这样增加了代码的复用性。
*/
public interface AccountMapper {
/**
* 账户转账业务
* @param fromActno 转出账户
* @param toActno 转入账户
* @param money 金额
*/
int transfer(String fromActno,String toActno,double money);
/**
* 查询用户
* @param id
* @return
*/
Account selectById(Long id);
}
实现类
//后续这个类(Mapper的实现类)可以不写了
public class AccountMapperImpl implements AccountMapper {
@Override
public int transfer(String fromActno, String toActno, double money) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//进行数据库操作
return 0;
}
@Override
public Account selectById(Long id) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//进行数据库操作
return null;
}
}
在处理业务的时候会执行多个操作数据库的sql语句,要确保使用的SqlSession是同一个,并且需要在业务代码都执行完的最后再提交事务。
如何确保是用一个SqlSession呢?
除了给Mapper层的每个方法添加一个SqlSession形参之外,还可以使用我们的ThreadLoad
业务逻辑处理开始之前创建SqlSession,然后将SqlSession对象放进到ThreadLoad中。
后续在持久层中需要用到的时候,直接在ThreadLoad中拿。
ThreadLoad对象定义在哪里呢?定义才SqlSessionUtil中的,定义成静态变量,getSqlSession方法获取的时候,先通过threadLoad.get()判断ThreadLoad中的sqlSession是否为空,如果为空的话就创建,然后threadLoad.set(sqlSession)放进ThreadLoad中,如果不为空就直接threadLoad.get()拿。 关闭的时候要记得从ThreadLoad中移除thread.remove()。
通过接口代理对象,使用mybatis框架的时候就不需要我们去创建Dao(Mapper)接口的实现类了。只要定义接口的方法,然后编写xml文件即可。
namespace:必须是Dao接口的全限定名称
id:必须是Dao接口的方法名
为什么呢:因为mybatis框架创建Dao接口的实现代理对象的时候,实现接口的方法的实现代码是通过自己的规则使用接口名和方法名去调用xml文件对应的sql语句的,默认将接口名全限定名称当作namespace,实现的方法名为id。
直接调用返回对象的相应得方法,就会执行xml文件中相应的sql语句。
#{}:底层使用PreparedStatement。特点:先进行SQL语句的编译,然后给SQL语句的占位符?传值。可以避免SQL注入的风险(优先使用这个,避免SQL注入的风险)
${}:底层使用Statement。特点:先进行SQL语句的拼接,然后再对SQL语句进行编译。存在SQL注入的风险(当需要接收前端动态传值的进行拼接sql语句的时候使用这个,例如 升序或降序 asc desc )。
1、用引号把通配符引出去,让#{}在外面好被jdbc检测到
2、使用concat()函数
在xml映射映射配置文件中类似于resultType=""属性赋值,是需要我们去写类的全限定名称的(namespace除外,namespace是不能使用别名的),为了简便,我们可以采用区别名的方法,即给全限定类名起一个简单短小的名字。在xml文件中使用。
配置方法:
在xml核心配置文件中的一个标签
8.4 Mapper的配置
左上角 File --> settings --> Editor --> File and Code Templates
然后点击 + 号 取名 粘贴模板内容 点击 ok。
下次需要的时候点击需要创建文件所在的目录文件夹使用快捷键,ALT + insert 找到模板创建文件就可了。
在xml映射文件中配置
parameterType表示传入参数的类型,这里可以用别名Long,官方文档有别名可以参考,Long类型是mybatis已经提供了别名的。(这个parameterType大部分情况下可以省略,mybatis会进行类型推断)
map类
POJO类
方法上有多个参数的时候 parameterType就可以不写了。
传参方法有两种:
第一种:如果是多个参数的话,mybatis框架的底层会创建一个Map集合,并且Map集合是以这种方式存储参数的:(两种key都可以拿,属于同一个map集合)。
map.put("arg0",name); map.put("arg1",age); map.put("arg2",sex);
map.put("param1",name); map.put("param2",age); map.put("param3",sex);
因此取值方式就和Map的取值方式一样,只不过key是按照mybatis创建的去取,arg0、arg1...或者param1、param2。
第二种:通过注解@Param
在接口的方法形参前面加上@Param注解 指定这个参数在map集合中对应的key是什么。
使用了@Param注解后 arg0、arg1没有了 但是param1、param2还是可以用的。
将查询的数据封装结果集的操作 resultType=”“
写法和返回一条数据一样,resultType指定实体类就行,如果是多条mybatis会封装成List集合。但是接口方法上的返回值类型需要是List集合。 一条记录却可以用List集合接收。
一条数据 Map
封装成大Map Map
结果集映射使用:
在xml核心配置文件中配置:
有时候我们有需求,一个接口的方法可以动态的执行SQL语句,而不是写死的SQL语句,这时候就需要我们使用动态SQL了。
例如多条件查询,比如查询的时候我们根据某个字段查询,根据日期查,或者根据主键和日期查询,或者根据主键和名字查询.....等,这么多的组合条件,如果是写死的SQL语句的话,我们要对应每种情况去写一条SQL语句,这样就会很麻烦,影响开发效率。
这时候动态SQL就能发挥它的作用了。
if 标签 配合这个 where标签的话 where 1=1 则不用写了,而且如果where后面 拼接了and 或者or的话,会去除掉。
主要使⽤在update语句当中,⽤来⽣成set关键字,同时去掉最后多余的“,”
⽐如我们只更新提交的不为空的字段,如果提交的数据是空或者"",那么这个字段我们将不更新。
//如果都不符合,就走这个
//等同于
if(){
}else if(){
}else if(){
}else if(){
}else{
}
批量删除
delete from t_car where id in
#{id}
批量插入
insert into t_car values
(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
sql标签⽤来声明sql⽚段
include标签⽤来将声明的sql⽚段包含到某个sql语句当中
类中存在另外一个类型的属性,如何将结果集直接映射呢?
column属性值写的是数据库的字段名
一般在mybatis-config.xml核心配置文件中设置全局懒加载选项
缓存:cache
缓存的作⽤:通过减少IO的⽅式,来提⾼程序的执⾏效率。
mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO。另⼀⽅⾯不再执⾏繁琐的查找算法。效率⼤⼤提升。
mybatis缓存包括:
* ⼀级缓存:将查询到的数据存储到SqlSession中。
* ⼆级缓存:将查询到的数据存储到SqlSessionFactory中。
* 或者集成其它第三⽅的缓存:⽐如EhCache【Java语⾔开发的】、Memcache【C语⾔开发的】
等。
缓存只针对于DQL语句,也就是说缓存机制只对应select语句。
一级缓存默认是开启的,不需要做任何配置。
原理:只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存。
思考:什么时候不走缓存?
* SqlSession对象不是同一个,不走缓存
* 查询条件不一样,肯定不走缓存
思考:什么时候缓存失效?
* 第一次DQL和第二次DQL之间做了以下两件事中的任意一件,都会让一级缓存清空
* 执行了sqlSession的clearCache()方法,这是手动清空缓存。
* 执行了Insert或者delete或者update语句,不论是操作哪张表,都会清空一级缓存。
⼆级缓存的范围是SqlSessionFactory。
使⽤⼆级缓存需要具备以下⼏个条件:
1、全局性地开启或关闭所有映射器配置⽂件中已配置
的任何缓存。默认就是true,⽆需设置。
.2、在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加配置:
3、使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝
4、SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中。此时⼆级缓存才可⽤。
eviction:指定从缓存中移除某个对象的淘汰算法。默认采⽤LRU策略。
LRU:Least Recently Used。最近最少使⽤。优先淘汰在间隔时间内使⽤频率最低的对象。(其
实还有⼀种淘汰算法LFU,最不常⽤。)
FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊⼆级缓存的对象最先被淘汰。
SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
flushInterval:
⼆级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存⾜够⼤,⼀直会向⼆级缓存中缓存数据。除⾮执⾏了增删改。
readOnly:
true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好。但是多线程并发可能
会存在安全问题。
false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般。但安
全。
size:
设置⼆级缓存中最多可存储的java对象数量。默认值1024。
集成EhCache是为了代替mybatis⾃带的⼆级缓存。⼀级缓存是⽆法替代的。
mybatis对外提供了接⼝,也可以集成第三⽅的缓存组件。⽐如EhCache、Memcache等。都可以。
EhCache是Java写的。Memcache是C语⾔写的。所以mybatis集成EhCache较为常⻅,按照以下步骤操
作,就可以完成集成:
第⼀步:引⼊mybatis整合ehcache的依赖。
org.mybatis.caches
mybatis-ehcache
1.2.2
ch.qos.logback
logback-classic
1.2.11
test
第⼆步:在类的根路径下新建echcache.xml⽂件,并提供以下配置信息
第三步:修改SqlMapper.xml⽂件中的标签,添加type属性。
所谓逆向工程是:根据数据库表逆向自动生成pojo类、Mapper类,Mapper.xml 文件自动生成增删改查代码。
1、再pom.xml文件中配置环境:
org.mybatis.generator
mybatis-generator-maven-plugin
1.4.1
true
mysql
mysql-connector-java
8.0.30
2、新建generatorConfig.xml逆向工厂代码生成文件
3、启动生成
public void test(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
//根据主键查询一个结果
Car car = mapper.selectByPrimaryKey(20L);
System.out.println(car);
//null表示没有条件所以是查询所有结果
List cars = mapper.selectByExample(null);
cars.forEach(car1 -> System.out.println(car));
// 根据条件查询结果(QBC query by criteria查询风格
// Example用于封装查询条件的,添加查询条件
CarExample carExample = new CarExample();
carExample.createCriteria()
.andBrandLike("比亚迪")
.andGuidePriceBetween(new BigDecimal(0),new BigDecimal(40));
//添加or
carExample.or().andCarTypeLike("技术车");
//最终sql语句:select * from t_car where (brand like "%比亚迪%" and guide_price>=0 and guide_price<40) or (car_type like "%技术车%")
List cars1 = mapper.selectByExample(carExample);
cars1.forEach(car1 -> System.out.println(car1));
session.close();
}
PageHelper插件可以很方便的管理和使用分页
依赖:
com.github.pagehelper
pagehelper
5.3.1
mybatis核心配置文件添加:
使用:
@Test
public void test(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
//查询第二页,前三条记录
PageHelper.startPage(2,3);
List cars = mapper.selectAll();
//查看分页信息,可以获取到分页的其他信息,例如 总条数等等...
PageInfo pageInfo = new PageInfo<>(cars);
System.out.println(pageInfo);
session.close();
}
注解式开发主要使用在简单的单表的CRUD操作,可以减少Sql映射文件的配置,但是不适合复杂操作。(简单使用注解,复杂的用xml)
@Insert( "insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})")
int insertCar(Car car);
@Delete("delete from t_car where id=#{id}")
int deleteById(Long id);
@Update("update t_car set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} where id=#{id}")
int updateById(Car car);
@Select("select * from t_car where id=#{id}")
Car selectById(Long id);
@Test
public void insertCar(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
Car car = new Car(null,"10377","报道33车1",35.0,"2022-12-05","技术车");
mapper.insertCar(car);
session.commit();
session.close();
}