以前的流程(功能简单,sql写在了Java代码内)
- 编写sql
- 预编译
- 设置参数
- 执行sql
- 封装结果
Mybaties会实现sql和编码分离写在配置文件内、sql由开发人员控制 是一个半自动化工具轻量级的框架
pom.xml
<?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>Mybaits</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybatis-01</module>
</modules>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
<?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>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<typeAlias type="Student" alias="Student"></typeAlias>
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- <package name="com.jian.java"></package>-->
<!-- <mapper resource="com/jian/java/StudentMapper.xml"></mapper>-->
<package name="com.jian.java"></package>
<!-- <mapper class="StudentMapper"></mapper>-->
</mappers>
</configuration>
<?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">
<!--
separator:表示分隔符
<foreach collection="" separator="," open="(" close=")" index="" item="a"></foreach>
-->
<!-- <select id="getALl" resultType="com.jian.java.Student">-->
<!-- select * from students where-->
<!-- <if test="sno!=null and sno.trim()!='' ">-->
<!-- sno=#{id}-->
<!-- </if>-->
<!--where只会去掉第一个出现的多出来的and 所以and写前面-->
<!-- <where>-->
<!-- <if test=""></if>-->
<!-- </where>-->
<!--
prefix="" trim整个拼串后的前缀
prefixOverrides="" 去掉前面多余的字符串
suffix=""
suffixOverrides=""
-->
<!--<trim prefix="where" prefixOverrides="and" suffixOverrides="and">-->
<!-- -->
<!--</trim>-->
<!--<choose>-->
<!-- <when test=""></when>-->
<!-- <otherwise></otherwise>-->
<!--</choose>-->
<!--
set标签可以去掉内容里多出来的逗号 where和它不用写 where和set
-->
<!-- </select>-->
<select id="getMap" resultType="map">
select * from students
</select>
<!-- <update id="update" parameterType="student">-->
<!-- update students-->
<!-- <set>-->
<!-- <if test="sno!=null">age=#{age+1}</if>-->
<!-- </set>-->
<!-- where sno=#{sno}-->
<!-- </update>-->
<!--<select id="getUser" resultType="student">-->
<!-- select * from students-->
<!--</select>-->
<!-- 可以将OGNL表达式的值绑定到一个遍量 方便引用 -->
<!-- <select id="">-->
<!-- <bind name="" value="'%'+lastname+'%'"/>-->
<!-- <include refid=""><property ></></include>-->
<!-- </select>-->
<!--<sql id="">-->
<!-- ${取出include定义的属性值} -->
<!--</sql>-->
import com.jian.java.Student;
import com.jian.java.StudentMapper;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Jiange
* @create 2021-04-20-14:15
*/
/**
* 在有多个参数的时候 会被封装成map key就是param1~paramn value就是我们传入的参数值
* 对于传入的参数是List或者数组的时候 也会封装成map key就是Collection 就是collection List就是list
* 数组的key就是array
* @Param("id") int id 这样时key就是id
* 或者我们可以直接传入一个map #{key}就可以取出值
*/
public class Mytest {
@Test
public void test1() throws IOException {
//根据配置文件创建工厂对象
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlsession实例、能直接执行已经映射的sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();//sqlsession代表和数据库的一次会话 用完关闭 不是线程安全的 每次使用都要重新获取 不能放在类的属性内
System.out.println(sqlSession);
//第一个参数是sql的唯一标识
try{
Student selectstu = sqlSession.selectOne("com.jian.java.selectstu",1);
System.out.println(selectstu);
}finally {
sqlSession.close();
}
}
@Test
public void test2() throws IOException {
String source = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(source);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = build.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//会为接口自动创建创建一个代理对象
Student byId = mapper.getById(1);
System.out.println(byId);//com.jian.java.Student{Sno='1', Sname='李子建', sex='男'}
}
@Test
public void test3() throws IOException {
String source = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(source);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();//不会自动提交
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//mybatis允许的增删改直接定义以下类型返回值 integer long Boolean void 记得要手动提交
// mapper.add(new Student("8","尚硅谷","男"));
mapper.delete("8");
sqlSession.commit();
sqlSession.close();
}
}
@Test
public void test4() throws IOException {
String resource = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// List<Student> all = mapper.getAll();
// all.forEach(System.out::println);
// Map<String, Object> returnMap = mapper.getReturnMap("2");
// System.out.println(returnMap);
Map<Integer, Student> stuMap = mapper.getStuMap();
System.out.println(stuMap);
}
// mybatis的两 个默认的内置参数
// _parameter:代表整个参数
// _databaseID:
@Test
public void test1(){
SqlSession sqlSession = get();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> user = mapper.getUser();
user.forEach(System.out::println);
}
/**
* mybatis有两级缓存
* 一级缓存: 本地缓存
* 与数据库同一次会话期间查询到数据会放在本地缓存中 SqlSession级别的缓存
* 以后如果要是获取相同的数据、直接从缓存里面拿 不用再去查询数据库
* 是一直开启的没法关闭
*
* 失效情况:
* 换了一个SqlSession
* SqlSession相同查询语句不同
* SqlSession相同、查询语句相同、两次查询期间执行了增删改操作
* 手动清楚了一级缓存的内容 sqlSession.clearCache();
* 二级缓存: 全局缓存 基于namespace的缓存 一个namespace对应一个二级缓存
* 工作机制
* 一个会话查询一条数据这个会话就会被放到当前的一级缓存中
* 如果会话关闭一级缓存就会被缓存到 二级缓存 新的查询就可以参照二级缓存里的内容
* 不同的namespace查出的数据就会被放在自己对应的缓存(map中)
* 查出的数据会被默认被放在一级缓存 只有会话提交或者关闭后 才会被放到二级缓存
* 使用:
* 开启耳机缓存的设置 <setting name="cacheEnabled" value="true"/> 默认是开启的
* 去mapp.xml配置二级缓存 <cache></cache>
* <cache eviction="" flushInterval="" type="" size="" readOnly="" blocking=""></cache>
* eviction:缓存的回收策略:
* flushInterval:缓存刷新的间隔
* 缓存多长时间清空一次、默认不清空 可以设置一个毫秒值
* readOnly:是否只读
* true:mybatis认为所有的缓存中获取数据的操作只是只读操作、不会修改数据
* 为了加快速度 会将缓存里的数据的引用交给用户 不安全
* false:mybatis会把缓存里的数据克隆一份 利用序列化和反序列化 安全 速度慢 默认就是false
* size表示缓存能放多少个元素
* type:指定自定义缓存的全类名
* 所有的类要实现序列化接口
*
* 注意要使用同一个 sqlSessionFactory
* 查询了的数据记得要提交事务 或者关闭连接
* 每一个select标签都有 useCache 默认是true false设置的是关闭二级缓存 flushCache默认是false 功能和下面这个一样
* 增删改标签有一个flushCache有一个属性 默认是true 表示执行后会清除缓存 一级和二级缓存都会清除
* sqlSession.clearCache();只会清除当前session的一级缓存
* localCacheScope、设置一级缓存的 取值 STATEMENT禁用一级缓存 session表示会存在会话缓存 默认session
*/
@Test
public void test1(){
SqlSession sqlSession = get().openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> all = mapper.getAll();
// sqlSession.commit();
List<Student> all1 = mapper.getAll();
System.out.println(all == all1);//true/false 这就是一级缓存 直接从缓存里拿的 不要提交事务 提交了 就是false
all.forEach(System.out::println);
// int delete = mapper.delete("6");
// System.out.println(delete);
//
// sqlSession.commit();
// sqlSession.close();
}
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"/>
<select id="getAll" resultType="student" useCache="true">
select * from students where sno = 1
</select>
/**
* SqlSessionFactory
* build->parse->将所有的config保存到一个对象 mapper也是
* 解析文件的每一个信息保存在configuration 返回configuration的DefaultSqlSessionFactory
* MappedStatement 代表一个sql的详细信息
* sqlsession
* 返回DefaultSqlSession 包含Executor和configuration;这一步会创建Executor
* 代理类接口对象
* getMapper 使用MapperProxyFactory创建一个MapperProxy的代理对象 代理对象包含了DefaultSqlSession(包含Executor)
*
* 执行增删改查:
* 调用invoke 判断增删改查类型 包装参数 即使是查询单个 也会调用selectList的第一个 最后还是会调用execute.query
* 查出来就会保存到本地缓存
*
*
* 总结:
* 1. 根据配置文件初始化出Configuraction对象
* 2. 创建DefaultSqlSession对象 包含Configuraction和Executor(根据配置文件的defaultExecutor)
* 3. 根据getMapper得到代理对象 对象里有(DefaultSqlSession)
* 4. 增删改查 会创建StateMent对象也会同时创建statementHandler 也会常见ParameterHandler
* 和ResultSetHandler
* ParameterHandler处理预编译参数以及设置参数值
* 5. 使用ResultSetHandler封装结果
* 插件原理:
* 四个处理器 创建时 都会被拦截器包装以后返回 插件可以为四个处理器创建出代理对象 AOP编程
* 代理对象就可以拦截四个对象每一个执行
*
* 插件的编写、
* 1.编写Interceptor的实现类
* 2.注解完成插件签名
* 3.将写好的插件注册到全局配置文件
* 多插件会逆序调用
*/
package com.jian.java;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.beans.Statement;
import java.util.Properties;
/**
* @author Jiange
* @create 2021-04-23-12:52
*/
//告诉拦截那个对象的哪个方法
@Intercepts(
{
@Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
}
)
public class MyPlugin implements Interceptor {
//拦截目标对象的方法执行
@Override
public Object intercept(Invocation invocation) throws Throwable {
//执行目标方法
Object proceed = invocation.proceed();
System.out.println("invocation"+invocation.getMethod());
//返回执行后的返回值
return proceed;
}
//包装目标对象 为目标对象创建一个代理对象
@Override
public Object plugin(Object o) {
Object wrap = Plugin.wrap(o, this);
System.out.println("plugin:"+o);
//返回当前处理器对象的代理对象
return wrap;
}
//将插件注册时的property设置进来
@Override
public void setProperties(Properties properties) {
System.out.println(properties);
}
}
package com.jian.java;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author Jiange
* @create 2021-04-23-15:33
*/
public class MyTypeHandler implements TypeHandler {
/**
* 定义当前的数据如何保存到数据库内
* @param preparedStatement
* @param i
* @param o
* @param jdbcType
* @throws SQLException
*/
@Override
public void setParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i,o.getCode());
}
@Override
public Object getResult(ResultSet resultSet, String s) throws SQLException {
return null;
}
@Override
public Object getResult(ResultSet resultSet, int i) throws SQLException {
return null;
}
@Override
public Object getResult(CallableStatement callableStatement, int i) throws SQLException {
return null;
}
}
@Test
public void test5(){
//批量操作
SqlSessionFactory sqlSessionFactory = get();
//可以批量操作的sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
}
/**
* statementType="CALLABLE"
* {call 名字()}
* <typeHandlers>
* <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType=""></typeHandler>
* </typeHandlers>
*/