自定义持久层框架
-
思路分析
-
框架使用端
- 创建SqlMapConfig.xml配置文件:数据库配置信息 存放mapper.xml
- 创建mapper.xml配置文件:存放sql信息,参数类型,返回值类型
-
自定义框架:本质就是对JDBC进行封装
-
加载配置文件
创建Resources类:负责加载配置文件,加载成字节输入流,存到内存中
-
创建两个javaBean(容器对象)
Configuration:全局配置类:存储sqlMapConfig.xml配置文件解析出来的内容
MappedStatement:映射配置类:存储mapper.xml配置文件解析出来的内容
-
解析配置文件,填充容器对象
创建SqlSessionFactoryBuilder类
方法:SqlSessionFactory
-
创建SqlSessionFactory接口及DefaultSqlSessionFactory
-
创建SqlSession接口和DefaultSqlSession实现类
-
-
图太难画了画不明白 分享一个博主的文章讲的很详细:www.cnblogs.com/shamo89/p/9…
框架使用端
- 框架所有端需要的配置如下
配置文件 sqlMapConfig.xml
<configuration>
<!--配置数据库信息-->
<dataSource>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bjpowernode?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
<!--引入映射配置文件-->
<mappers>
<mapper resource="mapper/EmpMapper.xml"></mapper>
</mappers>
</configuration>
sql配置文件
<mapper namespace="dept">
<select id="selectList">
select * from dept;
</select>
<select id="selectOne" resultType="com.zwf.pojo.Dept" parameterType="com.zwf.pojo.Dept">
select * from dept where DEPTNO=#{DEPTNO}
</select>
</mapper>
导入自定义框架的jar包
<?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">
<parent>
<artifactId>mybaitDemo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ipersistent_test</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--这里是通过Maven导入自定义项目的jar包-->
<dependency>
<artifactId>mybaitDemo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
测试类
package com.hniu.test;
import com.hniu.io.Resources;
import com.hniu.sqlSession.SqlSession;
import com.hniu.sqlSession.SqlSessionFactory;
import com.hniu.sqlSession.SqlSessionFactoryBuilder;
import com.zwf.pojo.Dept;
import com.zwf.pojo.Emp;
import org.junit.Test;
import java.io.InputStream;
/**
* @author zwf
* @Package: com.hniu.test
* @ClassName: IpersistentTest
* @Description:
* @date 2023/5/31 20:46
*/
public class IpersistentTest {
@Test
public void test1(){
//加载全局配置文件为字节输入流
InputStream resourcesAsSteam = Resources.getResourcesAsSteam("sqlMapConfig.xml");
//1.解析了配置文件,封装的Configuration对象 2.创建了sqlSessionFactory对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourcesAsSteam);
//通过SessionFactory创建SqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
Dept dept = new Dept();
dept.setDEPTNO(10);
Dept dept1 = (Dept) sqlSession.selectOne("dept.selectOne", dept);
System.out.println(dept1);
//释放资源
sqlSession.close();
}
}
部门类Dept
package com.zwf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zwf
* @Package: com.zwf.pojo
* @ClassName: Dept
* @Description:
* @date 2023/6/10 15:28
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Dept {
private Integer DEPTNO;
private String DNAME;
private String LOC;
}
数据库信息
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`DEPTNO` int(2) NOT NULL,
`DNAME` varchar(14) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`LOC` varchar(13) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`DEPTNO`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON');
SET FOREIGN_KEY_CHECKS = 1;
运行结果
Dept(DEPTNO=10, DNAME=ACCOUNTING, LOC=NEW YORK)
自定义框架
-
加载配置文件
package com.hniu.io; import java.io.InputStream; /** * @author zwf * @Package: com.hniu.io * @ClassName: Resources * @Description: * @date 2023/5/30 22:47 */ public class Resources { /** * 根据配置文件路径,加载配置文件成字节输入流,存到内存中 * @param path * @return */ public static InputStream getResourcesAsSteam(String path){ return Resources.class.getClassLoader().getResourceAsStream(path); } } -
创建两个javaBean容器对象
package com.hniu.pojo; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * @author zwf * @Package: com.hniu.pojo * @ClassName: Configuration * @Description: 存放全局配置文件 * @date 2023/5/31 21:06 */ public class Configuration { //数据源对象 private DataSource dataSource; //key:statementId:namespace.id MappedStatement:封装好的MappedStatement对象 private Map<String ,MappedStatement> mappedStatementMap=new HashMap<>(); public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public Map<String, MappedStatement> getMappedStatementMap() { return mappedStatementMap; } public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) { this.mappedStatementMap = mappedStatementMap; } }package com.hniu.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author zwf * @Package: com.hniu.pojo * @ClassName: MappedStatement * @Description: 映射配置类:存放mapper.xml解析内容 * @date 2023/5/31 21:03 */ @Data @NoArgsConstructor @AllArgsConstructor public class MappedStatement { //唯一标识 statementId:namespace.id private String statementId; //返回值类型 private String resultType; //参数值类型 private String parameterType; //sql语句 private String sql; } -
解析配置文件,填充容器对象
SqlSessionFactory 接口对象
package com.hniu.sqlSession; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: SqlSessionFactory * @Description: * @date 2023/5/31 21:22 */ public interface SqlSessionFactory { }DefaultSqlSessionFactory SqlSessionFactory对象的实现类
package com.hniu.sqlSession; import com.hniu.pojo.Configuration; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: DefaultSqlSessionFactory * @Description: * @date 2023/6/2 22:15 */ public class DefaultSqlSessionFactory implements SqlSessionFactory{ private Configuration configuration; //通过有参构造传递configuration对象 DefaultSqlSessionFactory(Configuration configuration){ this.configuration=configuration; } }SqlSessionFactoryBulider构建SqlSessionFactory工厂对象
package com.hniu.sqlSession; import com.hniu.config.XMLConfigBuilder; import com.hniu.pojo.Configuration; import org.dom4j.DocumentException; import java.io.InputStream; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: SqlSessionFactoryBuilder * @Description: * @date 2023/5/31 21:21 */ public class SqlSessionFactoryBuilder { /** * 1.解析配置文件,封装容器对象 * 2.创建SqlSessionFactory工厂对象 * @param inputStream * @return */ public SqlSessionFactory build(InputStream inputStream){ //解析配置文件,封装容器对象 XMLConfigBuilder:专门解析核心配置文件的解析类 XMLConfigBuilder xmlConfigBuilder=new XMLConfigBuilder(); Configuration configuration = null; try { //封装容器 configuration=xmlConfigBuilder.parse(inputStream); } catch (DocumentException e) { e.printStackTrace(); } //创建sqlSessionFactory工厂对象 SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration); return sqlSessionFactory; } }XMLConfigBuilder 解析全局配置文件
package com.hniu.config; import com.alibaba.druid.pool.DruidDataSource; import com.hniu.io.Resources; import com.hniu.pojo.Configuration; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.List; import java.util.Properties; /** * @author zwf * @Package: com.hniu.config * @ClassName: XMLConfigBuilder * @Description: * @date 2023/5/31 21:27 */ public class XMLConfigBuilder { private Configuration configuration; public XMLConfigBuilder(){ this.configuration=new Configuration(); } /** * 使用dom4j+xpath解析配置文件,封装Configuration对象 * @param inputStream * @return */ public Configuration parse(InputStream inputStream) throws DocumentException { Document document = new SAXReader().read(inputStream); Element rootElement = document.getRootElement(); //<property name="driverClassName" value="com.mysql.jdbc.Driver"></property> List<Element> list = rootElement.selectNodes("//property"); Properties properties = new Properties(); for (Element element : list) { String name = element.attributeValue("name"); String value = element.attributeValue("value"); properties.setProperty(name,value); } //创建Druid数据源对象 DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(properties.getProperty("driverClassName")); druidDataSource.setUrl(properties.getProperty("url")); druidDataSource.setUrl(properties.getProperty("username")); druidDataSource.setUrl(properties.getProperty("password ")); //给配置类dataSource赋值 configuration.setDataSource(druidDataSource); /*解析映射配置文件*/ //1.获取映射配置文件 <mapper resource="mapper/EmpMapper.xml"></mapper> List<Element> elements = rootElement.selectNodes("//mapper"); //2.根据路径进行映射配置文件的加载解析 for (Element element : elements) { String mapperPath = element.attributeValue("resource"); InputStream resourcesAsSteam = Resources.getResourcesAsSteam(mapperPath); //XMLMapperBuilder:专门解析配置文件的对象 XMLMapperBulider xmlMapperBulider = new XMLMapperBulider(configuration); //3.封装到MappedStatement xmlMapperBulider.parse(resourcesAsSteam); } return configuration; } }XMLMapperBuilder 解析XMLMapper映射文件
package com.hniu.config; /** * @author zwf * @Package: com.hniu.config * @ClassName: XMLMapperBulider * @Description: * @date 2023/6/1 22:10 */ import com.hniu.pojo.Configuration; import com.hniu.pojo.MappedStatement; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.List; /** * parse:解析映射配置文件-->mappedStatement--->configuration里面的map集合中 */ public class XMLMapperBulider { private Configuration configuration; public XMLMapperBulider(Configuration configuration) { this.configuration=configuration; } public void parse(InputStream resourcesAsSteam) throws DocumentException { Document document = new SAXReader().read(resourcesAsSteam); Element rootElement = document.getRootElement(); /** *<mapper namespace="emp"> * <select id="selectList"> * select * from emp; * </select> * <select id="selectOne"> * select * from emp where EMPNO=#{id} * </select> * </mapper> */ List<Element> list = rootElement.selectNodes("//select"); String namespace = rootElement.attributeValue("namespace"); for (Element element : list) { String id = element.attributeValue("id"); String resultType = element.attributeValue("resultType"); String parameterType = element.attributeValue("parameterType"); String sql = element.getTextTrim(); //封装mapperStatement对象 MappedStatement mappedStatement = new MappedStatement(); String statementId=namespace+"."+id; //接口包名加方法名 mappedStatement.setStatementId(statementId); mappedStatement.setParameterType(parameterType); mappedStatement.setResultType(resultType); mappedStatement.setSql(sql); //将封装好的mappedStatement封装到configuration中的map集合中 configuration.getMappedStatementMap().put(statementId,mappedStatement); } } } -
创建SqlSessionFactory接口及DefaultSqlSessionFactory实现类
Executor 执行器接口
package com.hniu.exector; /** * @author zwf * @Package: com.hniu.exector * @ClassName: executor * @Description: * @date 2023/6/2 22:54 */ public interface Executor { }SimpleExecutor执行器接口实现类
package com.hniu.exector; /** * @author zwf * @Package: com.hniu.exector * @ClassName: SimpleExecutor * @Description: * @date 2023/6/2 22:55 */ public class SimpleExecutor implements Executor{ }DefaultSession
package com.hniu.sqlSession; import com.hniu.exector.Executor; import com.hniu.pojo.Configuration; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: DefaultSqlSession * @Description: * @date 2023/6/2 22:50 */ public class DefaultSqlSession implements SqlSession{ private Configuration configuration;//存放全局配置信息和Mapper.xml映射信息 private Executor executor; //执行器 public DefaultSqlSession(Configuration configuration, Executor executor) { this.configuration = configuration; this.executor = executor; } }DefaultSqlSessionFactory中创建SqlSession openSession();
package com.hniu.sqlSession; import com.hniu.exector.Executor; import com.hniu.exector.SimpleExecutor; import com.hniu.pojo.Configuration; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: DefaultSqlSessionFactory * @Description: * @date 2023/6/2 22:15 */ public class DefaultSqlSessionFactory implements SqlSessionFactory{ private Configuration configuration; //通过有参构造传递configuration对象 DefaultSqlSessionFactory(Configuration configuration){ this.configuration=configuration; } /** * 生产sqlSession对象 * 创建执行器对象 * @return */ @Override public SqlSession openSession() { //创建执行器对象 Executor executor = new SimpleExecutor(); //生产SqlSession对象 并传递配置信息和执行器 SqlSession sqlSession = new DefaultSqlSession(configuration,executor); return null; } }创建SqlSession接口和DefaultSqlSession实现类
SqlSession
package com.hniu.sqlSession; import java.util.List; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: SqlSession * @Description: * @date 2023/6/2 22:50 */ public interface SqlSession { /** * 查询多个结果 * @param statementId * @param param * @param <E> * @return */ <E> List<E> selectList(String statementId,Object param); /** * 查询单个结果 * @param <T> * @param statementId * @param param * @return */ <T> Object selectOne(String statementId, Object param); /** * 清空资源 */ void close(); }DefaultSqlSession
package com.hniu.sqlSession; import com.hniu.exector.Executor; import com.hniu.pojo.Configuration; import com.hniu.pojo.MappedStatement; import java.util.List; /** * @author zwf * @Package: com.hniu.sqlSession * @ClassName: DefaultSqlSession * @Description: 实现SqlSession接口通过执行器类操作JDBC执行增删改查 * @date 2023/6/2 22:50 */ public class DefaultSqlSession implements SqlSession{ private Configuration configuration; private Executor executor; public DefaultSqlSession(Configuration configuration, Executor executor) { this.configuration = configuration; this.executor = executor; } @Override public <E> List<E> selectList(String statementId, Object param) { /*将查询操作委派给底层的执行器 * quer()执行底层的JDBC 1.数据库配置信息,2.sql配置信息 * */ MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId); List<E> list=executor.query(configuration,mappedStatement,param); return list; } @Override public <T> T selectOne(String statementId, Object param) { List<Object> list = this.selectList(statementId, param); if(list.size()==1) return (T) list.get(0); else if (list.size()>1){ throw new RuntimeException("返回结果过多"); } return null; } @Override public void close() { executor.close(); } } -
执行器
Executor接口
public interface Executor { <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object param); void close(); }接口实现SimpllExecutor
package com.hniu.exector; import com.hniu.config.BoundSql; import com.hniu.io.Resources; import com.hniu.pojo.Configuration; import com.hniu.pojo.MappedStatement; import com.hniu.pojo.ParameterMapping; import com.hniu.util.GenericTokenParser; import com.hniu.util.ParameterMappingTokenHandler; import lombok.SneakyThrows; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * @author zwf * @Package: com.hniu.exector * @ClassName: SimpleExecutor * @Description: * @date 2023/6/2 22:55 */ public class SimpleExecutor implements Executor{ private Connection connection; private PreparedStatement preparedStatement; private ResultSet resultSet; @Override public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object param) { try { //1加载驱动获取数据库连接 connection = configuration.getDataSource().getConnection(); //2获取preparedStatement对象 /**获取sql语句 * select * from emp where EMPNO=#{empNo} * 替换:select * from emp where EMPNO=? */ String sql = mappedStatement.getSql(); BoundSql boundSql=getBoundSql(sql); String finalSql = boundSql.getFinalSql(); //最终要执行的sql preparedStatement = connection.prepareStatement(finalSql); //设置参数 String parameterType = mappedStatement.getParameterType(); Class<?> parmeterTypeClass = Class.forName(parameterType); List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList(); for (int i = 0; i < parameterMappingList.size(); i++) { ParameterMapping parameterMapping = parameterMappingList.get(i); //参数值 String paramName = parameterMapping.getContent(); //反射 Field declaredField = parmeterTypeClass.getDeclaredField(paramName); declaredField.setAccessible(true); Object value = declaredField.get(param); preparedStatement.setObject(i+1,value); } //4.执行sql,发起查询 resultSet = preparedStatement.executeQuery(); //5处理返回结果集 ArrayList<E> list = new ArrayList<>(); String resultType = mappedStatement.getResultType(); Class<?> resultTyoeClass = Class.forName(resultType); Object o = resultTyoeClass.newInstance(); while(resultSet.next()){ ResultSetMetaData metaData = resultSet.getMetaData(); for (int i=1;i<=metaData.getColumnCount();i++){ //字段名 String columnName = metaData.getColumnName(i); //字段值 Object value = resultSet.getObject(columnName); //属性描述器 PropertyDescriptor propertyDescriptor=new PropertyDescriptor(columnName,resultTyoeClass); Method writeMethod = propertyDescriptor.getWriteMethod(); //参数1:实例对象 参数2:要设置的值 writeMethod.invoke(o,value); } list.add((E) o); } return list; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 1.#{} 占位符替换成? 2.解析替换的过程中将#{}里面的值保存下来 * @param sql * @return */ private BoundSql getBoundSql(String sql) { //1.创建标记处理器:配合标记解析器完成标记处理解析工作 ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler(); //2.创建标记解析器 GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler); String finalSql = genericTokenParser.parse(sql); //#{}里面的值的一个集合 id username List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings(); return new BoundSql(finalSql,parameterMappings); } @Override public void close() { } }
mybatis配置详解
<?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>
<!--
MyBatis核心配置文件中,标签的顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,
environments?,databaseIdProvider?,mappers?
-->
<!--引入properties配置文件-->
<properties resource="jdbc.properties" />
<!--设置类型别名-->
<typeAliases>
<!--
typeAlias:设置某个类型的别名
属性:
type:设置需要设置别名的类型
alas:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类型且不区分大小写
-->
<!--<typeAlias type="com.atguigu.mybatis.pojo.User" />-->
<!--以包为单位,将包下所有的类型设置默认的类型别名,即类名且不区分大小写-->
<package name="com.atguigu.mybatis.pojo"/>
</typeAliases>
<!--
environments:配置多个连接数据库的环境
属性:
default:设置默认使用的环境的id
-->
<environments default="development">
<!--
environment:配置某个具体的环境
属性:
id:表示连接数据库的环境的唯一标识,不能重复
-->
<environment id="development">
<!--
transactionManager:设置事务管理方式
属性:
type="JDBC|MANAGER"
JDBC:表示当前环境中,执行SQL中,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动提交
MANAGER:被管理,例如spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource:配置数据源
属性:
type:设置数据源的类型
type="POOLED|UNPOOLED|JNDI"
POOLED: 表示使用数据库连接池缓存数据库连接
UNPOOLED: 表示不适用数据库连接池
JNDI: 表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<!--driver:驱动内容-->
<property name="driver" value="${jdbc.driver}"/>
<!--连接数据库的url-->
<property name="url" value="${jdbc.url}"/>
<!--用户名-->
<property name="username" value="${jdbc.username}"/>
<!--密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--<mapper resource="mappers/UserMapper.xml" />-->
<!--
以包为单位引入映射文件
要求:
1、mapper接口所在的包要和映射文件所在的包一致
2、mapper接口和映射文件的名字一致
-->
<package name="com.atguigu.mybatis.mapper"/>
</mappers>
</configuration>
核心配置文件
mappers(映射器)
-
通过resource加载单个映射文件
<!-- 加载映射文件--> <mappers> <!--加载单个类路径下的映射文件--> <mapper resource="com/hniu/mapper/empMapper.xml"></mapper> </mappers> -
使用完全限定路径
<!-- 加载映射文件--> <mappers> <!--使用完全限定路径:这里注意必须要在empMapper.xml的绝对路径名前加上(file:///)的前缀--> <mapper url="file:///F:\2022毕业知识总结\微服务\spring系列\mybaitDemo\mybatisOne\src\main\java\com\hniu\mapper\empMapper.xml"></mapper> </mappers> -
使用mapper接口类路径
<!-- 加载映射文件--> <mappers> <!--使用类路径加载单个映射文件--> <mapper class="com.hniu.mapper.empMapper"></mapper> </mappers>注意前提是Mapper.xml文件和接口类在一个包下 并且还有一个前提是:使用的是mapper代理方法
-
使用包名引入
<!-- 加载映射文件--> <mappers> <!--使用包名引入--> <package name="com.hniu.mapper"/> </mappers>注意前提是Mapper.xml文件和接口类在一个包下 并且还有一个前提是:使用的是mapper代理方法
properties(属性)
-
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
<!-- 从resources目录下自动引入配置文件--> //方式一 默认从resources根目录查找 <properties resource="db.properties"/> //方式二指定路径查找db.properties文件 <properties resource="com/hniu/mapper/db.properties"/> //使用绝对路径查找db.properties文件 主要路径前面要加file:/// (表示本地文件传输协议) <!-- 1、IT中的File,本地文件传输协议,File协议主要用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件一样。 2、INUX 命令File,file 命令读取用 File 参数或者 FileList 变量指定的文件,在每个文件上执行一系列测试,然后将它们按照类型分类。 3、要使用File协议,基本的格式如下:file:///文件路径,比如要打开F:盘flash文件夹中的1.swf文件,那么可以在资源管理器或IE地址栏中键入:file:///f:/flash/1.swf并回车。 4、然后此命令将文件类型写入标准输出。文件可以是常规文件、目录、FIFO(指定的管道)、块特殊文件、字符特别文件、符号链接或者套接字类型。在漫画中也指"话" --> <properties url="file:///F:\2022毕业知识总结\微服务\spring系列\mybaitDemo\mybatisOne\src\main\resources\com\hniu\mapper\db.properties"/><!-- 在xml自定义properties文件--> <properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/bjpowernode?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </properties>driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/bjpowernode?useSSL=true&useUnicode=true&characterEncoding=utf8 username=root password=123456自定义的properties默认从resoures根路径下查找properties文件
-
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Emp" type="com.hniu.pojo.Emp"/>
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.hniu.pojo"/>
</typeAliases>
每一个在包 com.hniu.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.hniu.pojo.Emp 的别名为 emp;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
setting(设置)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
| 设置名 | 描述 | 有效值 | 默认值 |
|---|---|---|---|
| cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
| aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 | true | false | false (在 3.4.1 及之前的版本中默认为 true) |
| multipleResultSetsEnabled | 是否允许单个语句返回多结果集(需要数据库驱动支持)。 | true | false | true |
| useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
| useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
| autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
| autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException) | NONE, WARNING, FAILING | NONE |
| defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
| defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
| defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
| defaultResultSetType | 指定语句默认的滚动策略。(新增于 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) | 未设置 (null) |
| safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
| safeResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
| mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
| localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
| jdbcTypeForNull | 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
| lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
| defaultScriptingLanguage | 指定动态 SQL 生成使用的默认脚本语言。 | 一个类型别名或全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
| defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) | 一个类型别名或全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
| callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 | true | false | false |
| returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) | true | false | false |
| logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
| proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB (3.5.10 起废弃) | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
| vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
| useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) | true | false | true |
| configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) | 一个类型别名或完全限定类名。 | 未设置 |
| shrinkWhitespacesInSql | 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) | true | false | false |
| defaultSqlProviderType | 指定一个拥有 provider 方法的 sql provider 类 (新增于 3.5.6). 这个类适用于指定 sql provider 注解上的type(或 value) 属性(当这些属性在注解中被忽略时)。 (e.g. @SelectProvider) | 类型别名或者全限定名 | 未设置 |
| nullableOnForEach | 为 'foreach' 标签的 'nullable' 属性指定默认值。(新增于 3.5.9) | true | false | false |
| argNameBasedConstructorAutoMapping | 当应用构造器自动映射时,参数名称被用来搜索要映射的列,而不再依赖列的顺序。(新增于 3.5.10) | true | false | false |
一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" 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="safeResultHandlerEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
<setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
<setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
<setting name="callSettersOnNulls" value="false"/>
<setting name="returnInstanceForEmptyRow" value="false"/>
<setting name="logPrefix" value="exampleLogPreFix_"/>
<setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
<setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
<setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
<setting name="useActualParamName" value="true"/>
<setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>
类型处理器(typeHandlers)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
环境配置(environments)
<!-- 环境配置-->
<!--
environmments:配置多个连接数据库的环境
属性:
default:设置默认使用的环境的id
-->
<environments default="development">
<!-- environment :配置某个具体的环境
属性: id表示连接数据库的唯一标识不能重复
-->
<environment id="development">
<!-- 事务管理器
transactionManager:设置事务管理方式
属性:
type="JDBC|MANAGED"
JDBC:表示当前环境中,执行sql时,使用的是JDBC中原生的事务管理方式,提交和回滚需要手动处理
MANAGED:被管理
-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源
属性:
type:设置数据源的类型
POOLED:表示使用数据库连接池缓存数据库连接
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源
-->
<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>
应用篇
参数值获取
${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver} 会被静态替换为com.mysql.jdbc.Driver。#{}是sql的参数占位符,mybatis会将sql的#{}替换为?号,在sql执行前会使用PreparedStatement的参数值,比如ps.setInt(0,parameterValue),#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()
下面介绍使用通过${}和#{}获取参数的方式
-
mapper接口方法的参数为单个的子面量类型
- 可以通过
${}和{}以任意的名称获取参数值#{参数值},使用${}要添加单引号因为是字符拼接'${参数值}'
<select id="OneEmp" resultType="com.hniu.pojo.Emp" parameterType="java.lang.Integer"> select * from emp where EMPNO=#{sd}; <!--任意的名称--> </select>@Test public void MybatisTest2(){ InputStream in = DemeOneTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); empMapper mapper = sqlSession.getMapper(empMapper.class); Emp smith = mapper.OneEmp(7369); System.out.println(smith); }DEBUG 06-07 11:48:37,827 ==> Preparing: select * from emp where EMPNO=?; (BaseJdbcLogger.java:143) DEBUG 06-07 11:48:37,868 ==> Parameters: 7369(Integer) (BaseJdbcLogger.java:143) DEBUG 06-07 11:48:37,890 <== Total: 1 (BaseJdbcLogger.java:143) Emp(empNo=7369, eName=SMITH, job=CLERK, mgr=7902, hireDate=1980-12-17, sal=800.00, comm=null, beptNo=null) - 可以通过
-
mapper接口方法的参数为多个时
-
此时Mybatis会将这些参数放在一个map集合中,以两种方式进行存储
- 以arg0,arg1...为键,以参数为值
- 以param1,param2...为键,以参数为值
<select id="Login" resultType="com.hniu.pojo.Emp"> select * from emp where EMPNO=#{arg0} and ENAME=#{arg1}; </select>@Test public void MybatisTest1(){ InputStream in = DemeOneTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); empMapper mapper = sqlSession.getMapper(empMapper.class); Emp smith = mapper.Login(7369, "SMITH"); System.out.println(smith); }可选参数有[arg1, arg0, param1, param2] DEBUG 06-07 11:53:54,679 ==> Preparing: select * from emp where EMPNO=? and ENAME=?; (BaseJdbcLogger.java:143) DEBUG 06-07 11:53:54,776 ==> Parameters: 7369(Integer), SMITH(String) (BaseJdbcLogger.java:143) DEBUG 06-07 11:53:54,802 <== Total: 1 (BaseJdbcLogger.java:143) Emp(empNo=7369, eName=SMITH, job=CLERK, mgr=7902, hireDate=1980-12-17, sal=800.00, comm=null, beptNo=null)
-
-
若mapper接口方法的参数有多个时,可以手动将这些参数放在一个map中存储
- 只需要通过
#{}和${}以键的方式访问值即可,注意${}的单引号问题
<select id="Login" resultType="com.hniu.pojo.Emp"> select * from emp where EMPNO=#{1} and ENAME=#{2}; </select>public interface empMapper { //通过Empno和Ename获取员工信息 Emp Login(Map<String,Object> map); } @Test public void MybatisTest1(){ InputStream in = DemeOneTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); empMapper mapper = sqlSession.getMapper(empMapper.class); <!--自定义map--> HashMap<String, Object> map = new HashMap<>(); map.put("1","7369"); map.put("2","SMITH"); Emp smith = mapper.Login(map); System.out.println(smith); sqlSession.close(); }DEBUG 06-07 12:05:08,242 ==> Preparing: select * from emp where EMPNO=? and ENAME=?; (BaseJdbcLogger.java:143) DEBUG 06-07 12:05:08,285 ==> Parameters: 7369(String), SMITH(String) (BaseJdbcLogger.java:143) DEBUG 06-07 12:05:08,316 <== Total: 1 (BaseJdbcLogger.java:143) Emp(empNo=7369, eName=SMITH, job=CLERK, mgr=7902, hireDate=1980-12-17, sal=800.00, comm=null, beptNo=null) - 只需要通过
-
mapper接口方法的参数为实体类类型的参数
//通过Empno和Ename获取员工信息 Emp Login(Emp emp);<select id="Login" resultType="com.hniu.pojo.Emp"> select * from emp where EMPNO=#{empNo} and ENAME=#{eName}; </select>@Test public void MybatisTest3(){ InputStream in = DemeOneTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); empMapper mapper = sqlSession.getMapper(empMapper.class); Emp emp = new Emp(); emp.setEmpNo(7369); emp.setEName("SMITH"); //实体类类型参数赋值 Emp login = mapper.Login(emp); System.out.println(login); sqlSession.close(); } -
通过param注解
//通过Empno和Ename获取员工信息 Emp Login(@Param("empNo") Integer empNo,@Param("eName")String eName);<select id="Login" resultType="com.hniu.pojo.Emp"> select * from emp where EMPNO=#{empNo} and ENAME=#{eName}; </select>@Test public void MybatisTest3(){ InputStream in = DemeOneTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); empMapper mapper = sqlSession.getMapper(empMapper.class); //注解 Emp login = mapper.Login(7369,"SMITH"); System.out.println(login); sqlSession.close(); }
## 查询功能
返回值接收类型
package com.zwf.mapper;
import com.zwf.pojo.User;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @author zwf
* @Package: com.zwf.mapper
* @ClassName: UserMapper
* @Description:
* @date 2023/6/10 20:52
*/
public interface UserMapper {
//实体类接收
User getUserById(User user);
//list接收
List<User> getAllUser();
//基本类型接收
Integer getConunt();
//map
Map<String,Object> getUserByIdToMap(@Param("id")Integer id);
//list map
List<Map<String,Object>> getUserAllToMapList();
//注解
@MapKey("id")
Map<String,Object> getUserAllToMap();
}
<?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.zwf.mapper.UserMapper">
<select id="getUserById" resultType="user" parameterType="user">
select * from user where id=#{id};
</select>
<select id="getAllUser" resultType="user">
select * from user;
</select>
<select id="getConunt" resultType="int">
select count(*) from user;
</select>
<select id="getUserByIdToMap" resultType="hashmap" parameterType="int">
select * from user where id=#{id}
</select>
<select id="getUserAllToMap" resultType="hashmap">
select * from user;
</select>
<select id="getUserAllToMapList" resultType="hashmap">
select * from user;
</select>
</mapper>
@Test
public void testGetUserAllToMap(){
InputStream in = this.getClass().getClassLoader().getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> userByIdToMap = mapper.getUserAllToMap();
System.out.println(" Map<String, Object>:----"+userByIdToMap);
List<Map<String, Object>> userAllToMapList = mapper.getUserAllToMapList();
System.out.println("List<Map<String, Object>>---"+userAllToMapList);
Map<String, Object> userByIdToMap1 = mapper.getUserByIdToMap(1);
System.out.println(" Map<String, Object>----"+userByIdToMap1);
List<User> allUser = mapper.getAllUser();
System.out.println("List<User>"+allUser);
Integer conunt = mapper.getConunt();
System.out.println("Integer"+conunt);
User user = new User();
user.setId(1);
User userById = mapper.getUserById(user);
System.out.println("User"+userById);
}
Map<String, Object>:----{1={nickname=zwf, id=1, email=1405377877@qq.com}, 2={nickname=小米, id=2, email=123@qq.com}}
List<Map<String, Object>>---[{nickname=zwf, id=1, email=1405377877@qq.com}, {nickname=小米, id=2, email=123@qq.com}]
Map<String, Object>----{nickname=zwf, id=1, email=1405377877@qq.com}
List<User>[User(id=1, nickName=zwf, email=1405377877@qq.com, avatar=null), User(id=2, nickName=小米, email=123@qq.com, avatar=null)]
Integer2
UserUser(id=1, nickName=zwf, email=1405377877@qq.com, avatar=null)
进程已结束,退出代码为 0
模糊查询
-
实现模糊查询的sql编写方式有三种
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zwf.mapper.UserMapper"> <!--方式一--> <select id="getUserByLike2" resultType="user" > select * from user where nickname like concat('%',#{name},'%'); </select> <!--方式二--> <select id="getUserByLike1" resultType="user" > select * from user where nickname like "%"#{name}"%"; </select> <!--方式三--> <select id="getUserByLike" resultType="user" > select * from user where nickname like '%${name}%'; </select> </mapper>
动态设置表名
//动态设置表名
List<Map<String,Object>> getTable(@Param("tableName")String tableName);
<select id="getTable" resultType="map">
select * from ${tableName};
</select>
获取添加功能的自动递增的主键
//添加数据获取自动递增的主键
void addUser(User user);
//useGeneratedKeys:允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。
//keyProperty: keyProperty中对应的值是实体类的属性,而不是数据库的字段。
<insert id="addUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into user values (#{id},#{nickName},#{avatar},#{email});
</insert>
@Test
public void testAddUser(){
InputStream in = this.getClass().getClassLoader().getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User use = new User(null, "江城骏", null, "123@qqcom");
//添加用户
mapper.addUser(use);
//打印添加后的数据包括user主键
System.out.println(use); //User(id=12, nickName=江城骏, email=null, avatar=123@qqcom)
sqlSession.commit();
sqlSession.close();
}
多对一映射
级联属性赋值解决多对一映射关系
<?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.zwf.mapper.EmpMapper">
<resultMap id="empMapping" type="com.zwf.pojo.Emp">
//实体属性---数据库属性
<result property="dept.did" column="did"/>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<select id="getAllEmp" resultMap="empMapping">
select * from t_emp e left join t_dept d on e.did=d.did;
</select>
</mapper>
association解决多对一映射关系
<?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.zwf.mapper.EmpMapper">
<resultMap id="empMapping" type="com.zwf.pojo.Emp">
<id column="eid" property="eid"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="email" property="email"/>
//通过association标签
<association property="dept" javaType="com.zwf.pojo.Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
<select id="getAllEmp" resultMap="empMapping">
select * from t_emp e left join t_dept d on e.did=d.did;
</select>
</mapper>
分步查询解决多对一映射关系
第一步查询emp
<?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.zwf.mapper.EmpMapper">
<resultMap id="empMapping" type="com.zwf.pojo.Emp">
<association property="dept" column="did"
//这里要是唯一标识
select="com.zwf.mapper.DeptMapper.getByidDept"/>
</resultMap>
<select id="getAllEmp" resultMap="empMapping">
select * from t_emp;
</select>
</mapper>
第二步根据emp的did查询dept表的相关数据
<?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.zwf.mapper.DeptMapper">
<select id="getByidDept" resultType="com.zwf.pojo.Dept">
select *
from t_dept where did=#{did};
</select>
</mapper>
延迟加载
<settings>
<setting name="cacheEnabled" value="false"/>
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
-
测试类
package Test; import com.zwf.mapper.EmpMapper; import com.zwf.pojo.Emp; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; import java.util.List; /** * @author zwf * @Package: Test * @ClassName: EmpTest * @Description: * @date 2023/6/13 21:58 */ public class EmpTest { public static void main(String[] args) { InputStream in =EmpTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = build.openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); List<Emp> allEmp = mapper.getAllEmp(); //获取员工姓名 for (Emp emp : allEmp) { System.out.println(emp.getAge()); } } } -
当开启延迟加载后的日志
DEBUG 06-15 21:27:36,009 ==> Preparing: select * from t_emp; (BaseJdbcLogger.java:143) DEBUG 06-15 21:27:36,041 ==> Parameters: (BaseJdbcLogger.java:143) DEBUG 06-15 21:27:36,134 <== Total: 2 (BaseJdbcLogger.java:143) 12 45 -
关闭延迟加载后
DEBUG 06-15 21:28:56,366 ==> Preparing: select * from t_emp; (BaseJdbcLogger.java:143) DEBUG 06-15 21:28:56,412 ==> Parameters: (BaseJdbcLogger.java:143) DEBUG 06-15 21:28:56,428 ====> Preparing: select * from t_dept where did=?; (BaseJdbcLogger.java:143) DEBUG 06-15 21:28:56,428 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-15 21:28:56,428 <==== Total: 1 (BaseJdbcLogger.java:143) DEBUG 06-15 21:28:56,428 <== Total: 2 (BaseJdbcLogger.java:143) 12 45 进程已结束,退出代码为 0 -
总结在分步查询中开启延迟加载,当我们访问那些信息就会执行对应的sql,没有开启延迟加载则都执行
-
当个sql关闭延迟加载或者开启延迟加载可以通过fetchType="eager|lazy" ; eager 立刻加载 lazy延迟加载
<?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.zwf.mapper.EmpMapper"> <resultMap id="empMapping" type="com.zwf.pojo.Emp"> <association property="dept" column="did" fetchType="eager" //立刻加载 select="com.zwf.mapper.DeptMapper.getByidDept"/> </resultMap> <select id="getAllEmp" resultMap="empMapping"> select * from t_emp; </select> </mapper>
一对多映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--(配置)-->
<mapper namespace="dao.TeacherMapper">
<select id="getTeacher" resultType="teacher">
select * from teacher where id=#{ida}
</select>
<!-- ==============================按结果嵌套查询===============================================-->
<select id="getTeacher1" resultMap="TeacherStudent">
select s.id sid,s.name sname, s.tid stid ,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="studentList" ofType="student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="stid" property="tid"/>
</collection>
</resultMap>
<!-- ===================================按照查询嵌套处理=======================================-->
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where tid = #{tid}
</select>
</mapper>
- 关联 -association(多对一)
- 集合-collection(一对多)
- javaType & ofType
- javaType 用来指定实体类中属性的类型
- ofType用来指定映射到List或者集合中的实体类类型,泛型中的约束类型!
- 注意点:
- 保证SQL的可读性,尽量通俗易懂
- 主要一对多和多对一中,属性名和字段的问题!
- 使用日志排查错误
log4j日志文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
动态Sql
概述
动态SQL是指根据条件拼接SQL语句的功能,可以在SQL语句中添加或者删除某些条件和语句。在实际开发中,我们经常需要根据不同的条件拼接不同的SQL语句。如果只使用静态SQL,会使得代码冗余度高、可读性差、维护成本高等问题。而使用动态SQL可以很好地解决这些问题。
MyBatis中提供了很多种方式来实现动态SQL,包括if、choose、when、otherwise、trim、where、set等。
if标签
<select id="getUser" resultType="com.zwf.pojo.User">
select * from user where 1=1
<if test="id !=null">
and id=#{id}
</if>
<if test="nickname !=null and nickname!=''">
and nickname=#{nickname}
</if>
</select>
public class MybatisTest {
public static void main(String[] args) {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
user.setNickname("zwf");
User user1 = mapper.getUser(user);
System.out.println(user1);
}
}
DEBUG 06-17 22:06:57,133 ==> Preparing: select * from user where 1=1 and id=? and nickname=? (BaseJdbcLogger.java:143)
DEBUG 06-17 22:06:57,178 ==> Parameters: 1(Integer), zwf(String) (BaseJdbcLogger.java:143)
DEBUG 06-17 22:06:57,203 <== Total: 1 (BaseJdbcLogger.java:143)
User(id=1, nickname=zwf, email=1405377877@qq.com, avatar=null)
choose、when和otherwise标签
<select id="getUser" resultType="com.zwf.pojo.User">
select * from user where 1=1
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="nickname != null">
AND nickname = #{nickname}
</when>
<otherwise>
AND id =1;
</otherwise>
</choose>
</select>
public class MybatisTest {
public static void main(String[] args) {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
// user.setId(1);
// user.setNickname("zwf");
User user1 = mapper.getUser(user);
System.out.println(user1);
}
}
DEBUG 06-17 22:33:22,081 ==> Preparing: select * from user where 1=1 AND id =1; (BaseJdbcLogger.java:143)
DEBUG 06-17 22:33:22,129 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-17 22:33:22,152 <== Total: 1 (BaseJdbcLogger.java:143)
User(id=1, nickname=zwf, email=1405377877@qq.com, avatar=null)
choose、when和otherwise标签中,如果test条件成立,就会将当前标签中的SQL语句加入到最终的SQL语句中。只有一个可以成立,多个成立时按顺序第一个生效。
where
- 当where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或者or去掉
- 当where标签中没有内容时,此时where标签没有任何效果
- 注意:where标签不能将其中内容后面多余的and或者or去掉
trim
- prefix|suffix: 将trim标签中内容前面或后面添加指定内容
- suffixOverrides|prefixOverrider:将trim标签中内容前面或后面去掉指定内容
<select id="getUser" resultType="com.zwf.pojo.User">
select * from user
<where>
<trim prefix="or" prefixOverrides="or">
<if test="id!=null">
or id=#{id}
</if>
<if test="nickname != null">
and nickname=#{nickname}
</if>
</trim>
</where>
</select>
DEBUG 06-17 23:01:10,409 ==> Preparing: select * from user WHERE id=? and nickname=?
//因为where标签会去掉前面多余的and或者or
(BaseJdbcLogger.java:143)
DEBUG 06-17 23:01:10,459 ==> Parameters: 1(Integer), zwf(String) (BaseJdbcLogger.java:143)
DEBUG 06-17 23:01:10,484 <== Total: 1 (BaseJdbcLogger.java:143)
User(id=1, nickname=zwf, email=1405377877@qq.com, avatar=null)
=========================================将where标签去掉后的日志==================================
DEBUG 06-17 23:03:59,627 ==> Preparing: select * from user or id=? and nickname=? (BaseJdbcLogger.java:143)
DEBUG 06-17 23:03:59,682 ==> Parameters: 1(Integer), zwf(String) (BaseJdbcLogger.java:143)
foreach
- collection:设置需要循环的数组或集合
- item:表示数组或者集合中的每一个数据
- separator:循环体之间的分隔符
- open:foreach标签所循环的所有内容的开始符
- close:foreach标签所循环的所有内容的接收符
批量添加
//批量添加USer
int AllAddUser(@Param("users") List<User> users);
<insert id="AllAddUser" parameterType="com.zwf.pojo.User" >
insert into user values
<foreach collection="users" item="user" separator=",">
(#{user.id},#{user.nickname},#{user.avatar},#{user.email})
</foreach>
</insert>
public static void main(String[] args) {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<User>();
userList.add(new User(null,"xz","sj@qq.com",null));
userList.add(new User(null,"sd","s12j@qq.c3om",null));
userList.add(new User(null,"fg","sasj@qq.c345om",null));
Integer x= mapper.AllAddUser(userList);
System.out.println(x);
}
DEBUG 06-18 21:57:51,071 ==> Preparing: insert into user values (?,?,?,?) , (?,?,?,?) , (?,?,?,?) (BaseJdbcLogger.java:143)
DEBUG 06-18 21:57:51,106 ==> Parameters: null, xz(String), null, sj@qq.com(String), null, sd(String), null, s12j@qq.c3om(String), null, fg(String), null, sasj@qq.c345om(String) (BaseJdbcLogger.java:143)
DEBUG 06-18 21:57:51,118 <== Updates: 3 (BaseJdbcLogger.java:143)
3
批量删除
//批量删除
int AllDelUser(@Param("arr") Integer[] arr);
<delete id="AllDelUser" parameterType="int" >
delete from user where id in
<foreach collection="arr" item="item" close=")" open="(" separator=",">
#{item}
</foreach>
</delete>
public static void main(String[] args) {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int x = mapper.AllDelUser(new Integer[]{4, 9, 11, 13, 14});
System.out.println(x);
}
DEBUG 06-18 22:09:04,002 ==> Preparing: delete from user where id in ( ? , ? , ? , ? , ? ) (BaseJdbcLogger.java:143)
DEBUG 06-18 22:09:04,045 ==> Parameters: 4(Integer), 9(Integer), 11(Integer), 13(Integer), 14(Integer) (BaseJdbcLogger.java:143)
DEBUG 06-18 22:09:04,046 <== Updates: 0 (BaseJdbcLogger.java:143)
0
sql
sql片段
<sql id="user">
(#{user.id},#{user.nickname},#{user.avatar},#{user.email})
</sql>//设置sql片段
<insert id="AllAddUser" parameterType="com.zwf.pojo.User" >
insert into user values
<foreach collection="users" item="user" separator=",">
<include refid="user"></include>//引入sql片段
</foreach>
</insert>
缓存
一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
- 使一级缓存失效的四种情况:
- 不同的SqlSession对应不同的缓存
- 同一个SqlSession但是查询条件不相同
- 同一个SqlSession两次查询期间执行了任何一个增删改查操作
- 同一个SqlSession两次查询期间手动清空了缓存
测试
@Test
public void t2() {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (int i=0;i<2;i++){
//获取user集合列表
List list= mapper.getUser(new User());
//打印结果
System.out.println(list.get(1));
}
}
日志信息
DEBUG 06-18 22:47:24,514 ==> Preparing: select * from user (BaseJdbcLogger.java:143)
DEBUG 06-18 22:47:24,551 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-18 22:47:24,571 <== Total: 11 (BaseJdbcLogger.java:143)
User(id=2, nickname=小米, email=123@qq.com, avatar=null)
User(id=2, nickname=小米, email=123@qq.com, avatar=null)
从日志可以看出mybaits的一级缓存是默认开启的。当两次查询相同结果Sql只执行的一次
================================================执行添加操作=================================================
@Test
public void t2() {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (int i=0;i<2;i++){
//获取user集合列表
List list= mapper.getUser(new User());
//添加操作
List list1=new ArrayList<>();
list1.add(new User(null, "234", "33", null));
mapper.AllAddUser(list1);
//打印结果
System.out.println(list.get(1));
}
}
日志信息
DEBUG 06-18 22:45:07,205 ==> Preparing: select * from user (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,243 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,268 <== Total: 9 (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,277 ==> Preparing: insert into user values (?,?,?,?) (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,279 ==> Parameters: null, 234(String), null, 33(String) (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,288 <== Updates: 1 (BaseJdbcLogger.java:143)
User(id=2, nickname=小米, email=123@qq.com, avatar=null)
DEBUG 06-18 22:45:07,288 ==> Preparing: select * from user (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,289 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,293 <== Total: 10 (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,294 ==> Preparing: insert into user values (?,?,?,?) (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,295 ==> Parameters: null, 234(String), null, 33(String) (BaseJdbcLogger.java:143)
DEBUG 06-18 22:45:07,296 <== Updates: 1 (BaseJdbcLogger.java:143)
User(id=2, nickname=小米, email=123@qq.com, avatar=null)
同一个SqlSession两次查询期间执行了任何一个增删改查操作
==================================================手动清空缓存===============================================
@Test
public void t2() {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (int i=0;i<2;i++){
//获取user集合列表
List list= mapper.getUser(new User());
sqlSession.clearCache();//清空缓存
//打印结果
System.out.println(list.get(1));
}
}
日志信息
DEBUG 06-18 22:55:04,229 ==> Preparing: select * from user (BaseJdbcLogger.java:143)
DEBUG 06-18 22:55:04,258 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-18 22:55:04,275 <== Total: 11 (BaseJdbcLogger.java:143)
User(id=2, nickname=小米, email=123@qq.com, avatar=null)
DEBUG 06-18 22:55:04,276 ==> Preparing: select * from user (BaseJdbcLogger.java:143)
DEBUG 06-18 22:55:04,276 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-18 22:55:04,282 <== Total: 11 (BaseJdbcLogger.java:143)
User(id=2, nickname=小米, email=123@qq.com, avatar=null)
清空一级缓存
- SqlSession调用 close();
- 操作后SqlSession对象不可用,该对象的缓存数据也不可用
- SqlSession调用clearCache() | commit();
- 操作会清空一级缓存数据
- SqlSession调用增删改查方法;
- 操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据不准确。
二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFatory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled="true" ,默认为true,不需要设置
- 在映射文件中设置标签
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间查询了任意的增删改,会使一级和二级缓存同时失效
在核心配置文件中,设置全局配置属性cacheEnabled="true" ,默认为true,不需要设置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
实体类实现序列化接口
@AllArgsConstructor
@Data
@NoArgsConstructor
@ToString
public class User implements Serializable {
private Integer id;
private String nickname;
private String email;
private String avatar;
}
在映射文件中设置标签
<?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.zwf.mapper.UserMapper">
<cache/>#实现缓存
<select id="getUser" resultType="com.zwf.pojo.User">
select * from user
<where>
<trim prefixOverrides="and">
<if test="id!=null">
and id=#{id}
</if>
<if test="nickname != null">
and nickname=#{nickname}
</if>
</trim>
</where>
</select>
</mapper>
二级缓存必须在SqlSession关闭或提交之后有效
@Test
public void t3() {
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(MybatisTest.class.getResourceAsStream("mybaitsConfig.xml"));
SqlSession sqlSession = build.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> user = userMapper.getUser(new User());
System.out.println(user);
//将sqlSession关闭
sqlSession.close();
SqlSession sqlSession1 = build.openSession();
UserMapper UserMapper1 = sqlSession1.getMapper(UserMapper.class);
List<User> user1 = UserMapper1.getUser(new User());
System.out.println(user1);
sqlSession1.close();//将sqlSession关闭
}
DEBUG 06-20 21:43:44,565 Cache Hit Ratio [com.zwf.mapper.UserMapper]: 0.0 (LoggingCache.java:60)
DEBUG 06-20 21:43:45,084 ==> Preparing: select * from user (BaseJdbcLogger.java:143)
DEBUG 06-20 21:43:45,130 ==> Parameters: (BaseJdbcLogger.java:143)
DEBUG 06-20 21:43:45,169 <== Total: 6 (BaseJdbcLogger.java:143)
[User(id=1, nickname=zwf, email=1405377877@qq.com, avatar=null), User(id=2, nickname=小米, email=123@qq.com, avatar=null), User(id=12, nickname=江城骏, email=null, avatar=123@qqcom), User(id=15, nickname=xz, email=sj@qq.com, avatar=null), User(id=16, nickname=sd, email=s12j@qq.c3om, avatar=null), User(id=17, nickname=fg, email=sasj@qq.c345om, avatar=null)]
DEBUG 06-20 21:43:45,176 Cache Hit Ratio [com.zwf.mapper.UserMapper]: 0.5 (LoggingCache.java:60)
[User(id=1, nickname=zwf, email=1405377877@qq.com, avatar=null), User(id=2, nickname=小米, email=123@qq.com, avatar=null), User(id=12, nickname=江城骏, email=null, avatar=123@qqcom), User(id=15, nickname=xz, email=sj@qq.com, avatar=null), User(id=16, nickname=sd, email=s12j@qq.c3om, avatar=null), User(id=17, nickname=fg, email=sasj@qq.c345om, avatar=null)]
进程已结束,退出代码为 0
二级缓存相关属性
-
eviction属性:缓存回收策略
LRU :最近最小使用的:移除最长时间不被使用的对象
FIFO:先进先出:按对象进入缓存的顺序来移除它们。
SOFT-软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK-弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是LRU。
-
fulshlnterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
-
size属性:引用数目,正整数
代表缓存最多可以存储多小个对象,太大容易导致内存溢出
-
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
Mybatis缓存查询的顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存
Mybatis逆向工程
- 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
- 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
- Java实体类
- Mapper接口
- Mapper映射文件
创建逆向工程的步骤
-
添加依赖和插件
<dependencies> <!-- MyBatis核心依赖包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!-- junit测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <!-- log4j日志 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <!-- 控制Maven在构建过程中相关配置 --> <build> <!-- 构建过程中用到的插件 --> <plugins> <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.0</version> <!-- 插件的依赖 --> <dependencies> <!-- 逆向工程的核心依赖 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> </dependency> <!-- 数据库连接池 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.2</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> </dependencies> </plugin> </plugins> </build> -
创建逆向工程的配置文件generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- targetRuntime: 执行生成的逆向工程的版本 MyBatis3Simple: 生成基本的CRUD(清新简洁版) MyBatis3: 生成带条件的CRUD(奢华尊享版) --> <context id="DB2Tables" targetRuntime="MyBatis3Simple"> <!-- 数据库的连接信息 --> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/bjpowernode?useSSL=true&useUnicode=true&characterEncoding=utf8" userId="root" password="123456"> </jdbcConnection> <!-- javaBean的生成策略--> <javaModelGenerator targetPackage="com.atguigu.mybatis.pojo" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- SQL映射文件的生成策略 --> <sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\resources"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- Mapper接口的生成策略 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- 逆向分析的表 --> <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName --> <!-- domainObjectName属性指定生成出来的实体类的类名 --> <table tableName="t_emp" domainObjectName="Emp"/> <table tableName="t_dept" domainObjectName="Dept"/> </context> </generatorConfiguration> -
通过maven执行插件mybatis-generator