开源框架——Mybatis

发布于:2022-10-22 ⋅ 阅读:(1609) ⋅ 点赞:(2)

目录

第一部分 : 模仿Mybatis的自定义框架

1.1 原生JDBC

1.2 解决思路

1.3 设计

1.4 测试端实现

①在resources中编写核心配置文件和SQL片段文件

②编写实体类 

1.5 自定义持久层端的实现

①读取配置文件,并将文件以流的形式保存在流中

②解析配置信息,并使用Java类来存储基本信息 

③创建SqlSession对象,并调用方法进行查询

自定义持久层框架的改进

①在SqlSession接口中添加方法

②在DefaultSqlSession类中具体实现

③测试端编写与UserMapper.xml映射的接口,UserMapper.xml中的namespace属性值是该接口的全类名、SQL片段的id属性值是接口中的方法名

③测试动态代理的方式实现查询

第二部分 : Mybatis的基本使用

2.1 快速使用

2.1.1 开发步骤

2.1.2 Mybatis的增删改查注意点

2.1.3 基本Mybatis核心配置文件分析

2.1.4 核心配置文件标签分析

2.1.5 Mybatis相应API介绍

2.2 Mybatis的Dao层实现

第三部分 : Mybatis配置文件深入

3.1 核心配置文件SqlMapConfig.xml

3.1.1 Mybatis核心配置文件

第四部分 : Mybatis缓存

4.1 一级缓存

4.2 二级缓存

如何开启二级缓存?


第一部分 : 模仿Mybatis的自定义框架

1.1 原生JDBC

原生JDBC与数据库交互

  1. 加载数据库驱动

  2. 获取连接

  3. 定义SQL语句

  4. 获取预处理对象

  5. 设置参数

  6. 向数据库发出SQL请求

  7. 遍历并封装成实体对象

JDBC问题分析

  1. 数据库连接创建、释放资源频繁造成系统资源浪费,影响系统性能
  2. SQL语句在代码中硬编码,代码不易维护
  3. 预编译时向占位符传递参数时存在硬编码,不能自动传递,如果需要更改传递的参数,修改不易
  4. 结果集的解析存在硬编码,系统不易维护

1.2 解决思路

  1. 数据库连接池解决多次创建和释放连接对象时对IO资源的浪费
  2. 使用XML配置文件解决SQL语句及其它信息的硬编码
  3. 使用反射自动封装参数和结果集

1.3 设计

测试端

核心配置文件:

  • sqlMapConfig.xml : 存放数据库连接池配置信息
  • Mapper.xml : 存放SQL语句

Mapper.xml文件的路径配置到sqlMapConfig.xml

自定义持久层端

  1. 读取配置文件,并将文件以流的形式保存在流中
  2. 解析配置信息,并使用Java类来存储基本信息
  3. 使用工厂模式创建SqlSessionFactory接口,用来产生SqlSession实现对象
  4. 创建SqlSession接口,里面有相应的查询方法,如 : selectList,selectOne......
  5. 创建Executor接口,将JDBC代码进一步封装并在SqlSession的方法中调用Executor接口中的方法

1.4 测试端实现

①在resources中编写核心配置文件和SQL片段文件

sqlMapConfig.xml

<configuration>
    <dataSource>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/zdy_mybatis?serverTimezone=Asia/Shanghai"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </dataSource>
    <!--将UserMapper.xml文件的路径引用到全局配置文件-->
    <mapper resource="UserMapper.xml"></mapper>
</configuration>

UserMapper.xml 

<mapper namespace="user">

    <!--sql的唯一标识 : namespace.id组成 : statementId-->
    <select id="findAll" resultType="com.huier.domain.entity.User">
        select * from user
    </select>


    <select id="findByCondition" resultType="com.huier.domain.entity.User" paramterType="com.huier.domain.entity.User">
        select * from user where id = #{id} and username = #{username}
    </select>

</mapper>

②编写实体类 

public class User {
    private Integer id;
    private String username;
    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

1.5 自定义持久层端的实现

①读取配置文件,并将文件以流的形式保存在流中

  1. 创建Resource实体类
  2. 编写getResourceAsStream方法,获得传入的文件名,读取并存储文件信息到流中
    import java.io.InputStream;
    
    public class Resources {
    
        public static InputStream getResourceAsStream(String srcPath){
            //反射在四个阶段可以创建对象
            /*
                1.编译期间
                2.类加载准备阶段 Resources.class.getClassLoader()获得类加载器,并读取文件信息
                3.类加载阶段
                4.运行阶段
             */
            InputStream inputStream = Resources.class.getClassLoader().getResourceAsStream(srcPath);
            return inputStream;
        }
    }
InputStream resourceAsStream = Resources.getResourceAsStream(path);//将核心配置文件以及SQL配置文件皆以流的形式存储到内存中,方便后续解析

②解析配置信息,并使用Java类来存储基本信息 

  1. 创建Configuration类存储全局配置信息
  2. 创建MapperStatement类存储SQL信息,每一条SQL语句都是一个MapperStatement对象
  3. 创建XMLConfigBuilder类解析sqlMapConfig.xml配置文件并封装成JavaBean对象
  4. 创建XMLMapperBuilder类解析Mapper.xml配置文件并封装成JavaBean对象
  5. 创建SqlSessisonFactoryBuilder类完成文件解析、封装并生成SqlSessisonFactory实现类对象

Configuration类 : 配置类,存储数据源信息和SQL语句

@Data//lombok插件自动生成get set方法等
public class Configuration {

    private DataSource dataSource;//数据源

    /**
     * @Author HuiEr
     * @Date 2022/10/9 14:51
     * @Version 2020
     * @Description key : statementId[namespace.id]
     */
    private Map<String,MappedStatement> statementMap
            = new HashMap<String, MappedStatement>();

}

MapperStatement : 存储SQL语句信息

@Data
@AllArgsConstructor
public class MappedStatement {
    private String id;//唯一标识
    private String resultType;//结果类型
    private String paramType;//参数类型
    private String sql;//sql语句
}

SqlSessisonFactoryBuilder : 解析、封装JavaBean对象,生成SqlSessisonFactory对象

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream in) throws DocumentException {
        //解析xml 封装Configuration
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(in);//生成Configuration对象
        Configuration configuration = xmlConfigBuilder.parseConfig();//为对象的数据源属性赋值
        //创建sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
        return sqlSessionFactory;
    }
}

XMLConfigBuilder : 解析核心配置文件并封装成Configuration对象

public class XMLConfigBuilder {

    private Configuration configuration;//核心配置类
    private InputStream in;//xml的流文件

    public XMLConfigBuilder(InputStream in){
        this.in = in;
        configuration = new Configuration();
    }

    /**
     * @Author HuiEr
     * @Date 2022/10/9 15:03
     * @Version 2020
     * @Description 将配置文件解析并封装成javaBean
     */
    public Configuration parseConfig() throws DocumentException {
        Document document = new SAXReader().read(in);
        //<configuration>
        Element rootElement = document.getRootElement();
        List<Element> dataSourceList = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element element : dataSourceList) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name, value);
        }
        //使用连接池
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(properties.getProperty("driverClass"));
        dataSource.setUrl(properties.getProperty("jdbcUrl"));
        dataSource.setUsername(properties.getProperty("username"));
        dataSource.setPassword(properties.getProperty("password"));

        configuration.setDataSource(dataSource);

        //解析mapper.xml
        List<Element> mappers = rootElement.selectNodes("//mapper");
        for (Element mapper : mappers) {
            String resource = mapper.attributeValue("resource");
            InputStream resourceAsStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration,resourceAsStream);
            xmlMapperBuilder.parse();
        }
        return configuration;
    }
}

XMLMapperBuilder : 解析配置Mapper文件,将每一个SQL语句封装成MapperStatement对象

public class XMLMapperBuilder {
    private Configuration configuration;//核心配置类

    private InputStream in;

    public XMLMapperBuilder(Configuration configuration,InputStream in){
        this.configuration = configuration;
        this.in = in;
    }

    public void parse() throws DocumentException {
        Document document = new SAXReader().read(in);
        Element rootElement = document.getRootElement();//<mapper>
        String namespace = rootElement.attributeValue("namespace");
        List<Element> selectNodes = rootElement.selectNodes("//select");
        for (Element selectNode : selectNodes) {
            String id = selectNode.attributeValue("id");
            String resultType = selectNode.attributeValue("resultType");
            String paramterType = selectNode.attributeValue("paramterType");
            String sql = selectNode.getTextTrim();
            String statementId = namespace+"."+id;
            MappedStatement mappedStatement = new MappedStatement(id,resultType,paramterType,sql);
            configuration.getStatementMap().put(statementId, mappedStatement);
        }
    }
}

SqlSessisonFactoryBuilder接口 : 生产SqlSession对象

public interface SqlSessionFactory {

    public SqlSession openSession();
}

SqlSessisonFactoryBuilder接口实现类 : DefultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private Configuration configuration;//已经封装好核心配置类信息和SQL信息

    public DefaultSqlSessionFactory(Configuration configuration){
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}

 SqlSessison接口 : 与数据库连接查询的基础方法,如selectOne,selectList等

参数说明 : statementId : 定位SQL,由*Mapper.xml的namespace.id组成;Object...params : 传入的参数,这里使用实体类作为参数直接查询

public interface SqlSession {
    //查询所有
    public <T> List<T> selectList(String statementId,Object...params) throws Exception;

    //根据条件查询单个
    public <T> T selectOne(String statementId,Object...params) throws Exception;
}

 SqlSessison接口实现类 : DefaultSqlSessison

具体实现了接口的方法,在这里并没有直接在重写方法体中编写代码实现操作,而是调用Executor接口的方法来进一步实现与数据库的交互功能,可以极大的明确每个类的功能、方便维护、更改

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    public <T> List<T> selectList(String statementId, Object... params) throws Exception {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        List<Object> query = simpleExecutor.query(configuration, configuration.getStatementMap().get(statementId), params);
        return (List<T>) query;
    }

    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或者返回结果过多");
        }
    }

    public <T> T getMapper(Class<?> mapperClass) {
        //使用JDK动态代理
        Object instance = Proxy.newProxyInstance(
                DefaultSqlSession.class.getClassLoader(),
                new Class[]{mapperClass},
                new InvocationHandler() {
                    public Object invoke(Object proxy,//当前代理对象的引用
                                         Method method,//当前被调用方法的引用
                                         Object[] args) throws Throwable {
                        //准备参数 将接口全路径和namespace、方法名和id保持一致,可以拼装成statementId
                        String methodName = method.getName();
                        String className = method.getDeclaringClass().getName();
                        String statementId = className+"."+methodName;
                        Type genericReturnType = method.getGenericReturnType();
                        if(genericReturnType instanceof ParameterizedType){//类型是否有泛型
                            List<Object> objects = selectList(statementId, args);
                            return objects;
                        }
                        return selectOne(statementId, args);
                    }
                });
        return (T) instance;
    }
}
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);//读取流中的文件,并使用dom4j技术解析,并存储到JavaBean中,返回工厂实例对象

③创建SqlSession对象,并调用方法进行查询

  1. 使用SqlSessionFactory对象中的openSession()方法创建SqlSession对象
  2. 调用SqlSession中的方法与数据库进行交互查询

Executor接口 : 定义了具体的与数据库交互的操作 

参数说明 : Configuration configuration : 传入配置信息和SQL语句; MappedStatement mappedStatement : 获得本次查询的具体的SQL语句;Object...params : 查询时的参数,这里就是实体类对象

public interface Executor {

    public <T> List<T> query(Configuration configuration,
                             MappedStatement mappedStatement,
                             Object...params) throws Exception;
}

Executor接口实现类 : SimpleExecutor 

public class SimpleExecutor implements Executor {
    public <T> List<T> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        Connection connection = configuration.getDataSource().getConnection();

        String sql = mappedStatement.getSql();

        //替换并使用?作为占位符
        BoundSql boundSql = getBoundSql(sql);

        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());


        String paramType = mappedStatement.getParamType();
        Class<?> paramTypeClass = getClassType(paramType);
        List<ParameterMapping> mappings = boundSql.getMappings();
        for(int i = 0;i < mappings.size();i++){
            String content = mappings.get(i).getContent();//字段名

            //反射
            Field field = paramTypeClass.getDeclaredField(content);
            field.setAccessible(true);
            Object o = field.get(params[0]);
            preparedStatement.setObject(i+1, o);
        }

        ResultSet resultSet = preparedStatement.executeQuery();
        String resultType = mappedStatement.getResultType();
        Class<?> resClass = getClassType(resultType);
        ArrayList<Object> objects = new ArrayList();
        while (resultSet.next()){
            Object o = resClass.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();//元数据
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);//字段名
                Object object = resultSet.getObject(columnName);
                //使用反射
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o,object);
            }
            objects.add(o);
        }
        return (List<T>) objects;
    }

    private Class<?> getClassType(String paramType) throws ClassNotFoundException {
        if(paramType != null){
            Class<?> aClass = Class.forName(paramType);
            return aClass;
        }
        return null;
    }

    private BoundSql getBoundSql(String sql) {
        //标记处理类 : 配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser parser = new GenericTokenParser("#{", "}", tokenHandler);
        //解析出来的sql
        String parse = parser.parse(sql);
        //解析出来的参数名称
        List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
        return new BoundSql(parse, parameterMappings);
    }
}

getBoundSql(String sql) :解析原有的SQL并使用?作为占位符,返回BoundSql对象

private BoundSql getBoundSql(String sql) {
        //标记处理类 : 配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser parser = new GenericTokenParser("#{", "}", tokenHandler);
        //解析出来的sql
        String parse = parser.parse(sql);
        //解析出来的参数名称
        List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
        return new BoundSql(parse, parameterMappings);
    }

 BoundSql类 : 存储使用?作为占位符的SQL语句、替换前{}中的参数名称

归纳总结上述过程 : 1.以流的形式加载配置文件到内存中;2.解析流形成JavaBean对象,生成工厂对象;3.使用工厂对象创建SqlSession对象使用基础方法与数据库进行交互查询

 完整测试

String path = "sqlMapConfig.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = factory.openSession();
User user = new User();
user.setId(1);
user.setUsername("张三");
User selectUser = sqlSession.selectOne("user.selectOne", user);
List<Object> objects = sqlSession.selectList("user.selectList");
for (Object object : objects) {
    System.out.println(object);
}
System.out.println(selectUser);

 上述过程已经完成了一个简易的持久层框架,但是在调用方法进行查询时依旧存在硬编码

String path = "sqlMapConfig.xml";//xml文件地址
InputStream resourceAsStream = Resources.getResourceAsStream(path);//转换成流
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);//解析生成JavaBean
SqlSession sqlSession = factory.openSession();//获取SqlSession对象
User user = new User(1,'张三');
//调用查询单个方法
sqlSession.selectOne("User.selectOne",user);//仍然存在硬编码,需要手动拼接statementId去传入
/*
    statementId = UserMapper.xml中的namespace属性值.UserMapper.xml中SQL语句的id
*/

 对此,我们可以将整个Mapper.xml文件封装成一个相应的类,将namespace和类的全类名相同,每一个SQL语句的id和方法名对应,这样我们可以通过反射技术进行自动拼接。但是我们这个类需要有实体方法吗?检查这个类的作用仅仅只是作为一个标识符来定位具体的SQL片段,所以我们将这其定义成一个接口

自定义持久层框架的改进

使用代理模式对方法进行增强

①在SqlSession接口中添加方法

//生成动态代理对象
public <T> T getMapper(Class<?> mapperClass);

②在DefaultSqlSession类中具体实现

public <T> T getMapper(Class<?> mapperClass) {
        //使用JDK动态代理
        Object instance = Proxy.newProxyInstance(
                DefaultSqlSession.class.getClassLoader(),
                new Class[]{mapperClass},
                new InvocationHandler() {
                    public Object invoke(Object proxy,//当前代理对象的引用
                                         Method method,//当前被调用方法的引用
                                         Object[] args) throws Throwable {
                        //准备参数 将接口全路径和namespace、方法名和id保持一致,可以拼装成statementId
                        String methodName = method.getName();
                        String className = method.getDeclaringClass().getName();
                        String statementId = className+"."+methodName;
                        Type genericReturnType = method.getGenericReturnType();
                        if(genericReturnType instanceof ParameterizedType){//类型是否有泛型
                            List<Object> objects = selectList(statementId, args);
                            return objects;
                        }
                        return selectOne(statementId, args);
                    }
                });
        return (T) instance;
    }

③测试端编写与UserMapper.xml映射的接口,UserMapper.xml中的namespace属性值是该接口的全类名、SQL片段的id属性值是接口中的方法名

public interface UserMapper {

    public List<User> findAll() throws Exception;

    public User findByCondition(User user) throws Exception;
}
<mapper namespace="com.huier.mapper.UserMapper">

    <!--sql的唯一标识 : namespace.id组成 : statementId-->
    <select id="findAll" resultType="com.huier.domain.entity.User">
        select * from user
    </select>


    <select id="findByCondition" resultType="com.huier.domain.entity.User" paramterType="com.huier.domain.entity.User">
        select * from user where id = #{id} and username = #{username}
    </select>

</mapper>

③测试动态代理的方式实现查询

String path = "sqlMapConfig.xml";//xml文件地址
InputStream resourceAsStream = Resources.getResourceAsStream(path);//转换成流
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);//解析生成JavaBean
SqlSession sqlSession = factory.openSession();//获取SqlSession对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);//获取增强类
List<User> all = mapper.findAll();//直接调用方法即可

这样就实现了一个简易的持久层框架,框架的底层大量使用了反射相关技术,将原有JDBC的硬编码通过编写文件的方式解决、手动映射则用反射代替,大大降低了代码之间的耦合度,降低了编写重复代码的频率,提高了效率。

第二部分 : Mybatis的基本使用

2.1 快速使用

2.1.1 开发步骤

  1. 基于Maven工程实现快速体验,导入Mybatis坐标
            <!--mysql驱动,Mybatis实质还是对JDBC的封装需要mysql的支持-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.16</version>
            </dependency>
            <!--mybatis坐标-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.6</version>
            </dependency>
            <!--测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>compile</scope>
            </dependency>
  2. 创建实体类
    public class User implements Serializable {
        private Integer userId;
        private String userName;
        private Integer userAge;
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Integer getUserAge() {
            return userAge;
        }
    
        public void setUserAge(Integer userAge) {
            this.userAge = userAge;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "userId=" + userId +
                    ", userName='" + userName + '\'' +
                    ", userAge=" + userAge +
                    '}';
        }
    }
  3. 创建数据库与实体类对应的表
  4. 编写核心配置文件sqlMapConfig.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config
    3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
            <!--数据源环境-->
        <environments default="development"><!--指定默认的环境名称-->
            <environment id="development"><!--指定当前环境的名称-->
                <transactionManager type="JDBC"/><!--指定事务管理类型是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"/>
        </mappers>
    </configuration>
  5. 编写接口UserMapper和SQL文件UserMapper.xml
    public interface UserMapper {
    
        /*
        Mybatis代理的四要素:
            1.namespace必须是接口的全限定名
            2.方法名 == id值
            3.参数类型要一致
            4.返回类型要一致
         */
        List<User> findAll() throws IOException;
    }
    <?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">
    
    <!--namespace和接口全类名匹配-->
    <mapper namespace="mapper.UserMapper">
    
        <!--id和接口方法名匹配-->
        <select id="findAll" resultType="user">
            <include refid="selectUser"></include>
        </select>
    </mapper>
  6. 测试类测试
    //1.获得核心配置文件
    InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");
    //2.获得sqlSession工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
    //3.获得session回话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //获得UserMapper的代理对象,由MyBatis代理实现实例化对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.findAll();
    System.out.println(users);
    

2.1.2 Mybatis的增删改查注意点

  • 插入insert操作 :①使用insert标签;②指定参数类型parameterType = "参数类型";③SQL语句中使用#{实体属性名}作为占位符;④插入操作调用的API是sqlSession.insert("命名空间.id",实体对象)或代理对象的方式;⑤手动提交事务,sqlSession.commit()
  • 修改update操作 : ①使用update标签;②手动提交事务,sqlSession.commit()
  • 删除delete操作 : ①使用delete标签;②SQL语句中如果只有一个参数#{任意字符串}即可;③手动提交事务,sqlSession.commit()
  • 查询select操作 : ①使用select标签;②指定结果类型resultType = "结果类型"

2.1.3 基本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"><!--xml文件约束头-->

<configuration><!--根标签-->
       
</configuration>

2.1.4 核心配置文件标签分析

MyBatis核⼼配置⽂件层级关系

  • configuration
    • properties : 引入外部文件
    • settings : 配置
    • typeAliases : 实体类别名
    • typeHeadlers
    • objectFactory
    • plungins
    • enviroments : 环境
      • enviroment
        • transactionManager
        • dataSource
    • databasedProvider
    • mappers : 引入Mapper.xml文件

Mybatis常用配置解析

①environment,数据库环境的配置,支持多环境配置

<environments default="development"><!--指定默认的环境名称-->
        <environment id="development"><!--指定当前环境的名称-->
            <transactionManager type="JDBC"/><!--指定事务管理类型是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>

事务管理器类型有两种

  • JDBC : 直接使用JDBC的提交和回滚设置,它依赖于数据源得到的连接来管理事务作
    ⽤域
  • MANAGED

数据源类型有三种

  • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
  • POOLED:这种数据源的实现利⽤“池”的概念将 JDBC 连接对象组织起来。
  • JNDI:这个数据源的实现是为了能在如 EJB 或应⽤服务器这类容器中使⽤,容器可以集中或在外部配
    置数据源,然后放置⼀个 JNDI 上下⽂的引⽤。

②mapper,该标签的作用是加载映射,加载方式有如下几种

<mapper resource="相对路径"/>
<mapper url="绝对路径"/>
<mapper class="全类名"/>

<!--注意 : 如果使用包名来映射xml文件,则mappper接口的路径要和xml文件在同一个文件夹中-->
<package name="org.mybatis.builder"/> 

2.1.5 Mybatis相应API介绍

SqlSession⼯⼚构建器SqlSessionFactoryBuilder
常⽤API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核⼼⽂件的输⼊流的形式构建⼀个SqlSessionFactory对象

String resource = "核心配置文件";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

SqlSession工厂对象SqlSessionFactory

SqlSessionFactory有多个方法创建SqlSession实例,常用

  • openSession(),增删改时需要手动提交事务
  • openSession(boolean),传入true则表示,自动提交事务

SqlSession会话对象

执行语句的主要方法

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

操作事务的方法

void commit()
void rollback()

2.2 Mybatis的Dao层实现

  • 传统方式
  • 代理类

第三部分 : Mybatis配置文件深入

3.1 核心配置文件SqlMapConfig.xml

3.1.1 Mybatis核心配置文件

①Properties标签

实际开发中,习惯将数据源的配置信息单独抽取成⼀个properties⽂件,该标签可以加载额外配置的properties⽂件

②typeAliases标签

类型别名是为Java 类型设置⼀个短的名字,自动扫描该包下的类

第四部分 : Mybatis缓存

4.1 一级缓存

总结 : 查询操作首先从缓冲池中获取答案,如果没有则查询后将答案存入缓冲池;增删改操作都将清空缓冲池,防止数据的不合法

4.2 二级缓存

二级缓存的原理和一级缓存相同,第一次查询时,会将数据放入缓存中,第二次查询时则会直接从缓冲区中获取,在进行增删改操作后会清空缓存区,但是和一级缓存不同的时,缓存的范围会变得更大。一级缓存是以SqlSession为域对象,而二级缓存是以Mapper.xml文件的namespace作为域对象

二级缓存构建在一级缓存之上,在收到查询请求时,Mybatis首先会查询二级缓存,若二级缓存未命名,再去查询一级缓存,一级缓存没有,再查询数据库

与一级缓存不同,二级缓存和具体的命名空间相绑定,一个Mapper中只有一个Cache,相同Mapper中的MapperStatement(将SQL语句封装成一个MapperStatement对象)公用一个Cache,一级缓存则是和SqlSession相绑定

如何开启二级缓存?

  1. 开启二级缓存 : 和一级缓存不同的是,二级缓存需要手动开启,在核心配置文件添加
    <!--开启⼆级缓存-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  2. 需要在相应的Mapper.xml文件中开启缓存

    <!--开启⼆级缓存-->
    <cache></cache>

    这里可以使用Mybatis默认的缓存功能类,也可以使用自定义缓存。我们不写type就使用Mybatis默认的缓存,也可以去实现Cache接口来自定义缓存
    开启了⼆级缓存后,还需要将要缓存的pojo实现Serializable接⼝,为了将缓存数据取出执⾏反序列化操作,因为⼆级缓存数据存储介质多种多样,不⼀定只存在内存中,有可能存在硬盘中,如果我们要再取这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接⼝

  3. useCacheflushCache
    mybatis中还可以配置userCacheflushCache等配置项,userCache是⽤来设置是否禁⽤⼆级缓 存的,在statement中设置useCache=false可以禁⽤当前select语句的⼆级缓存,即每次查询都会发出 sql去查询,默认情况是true,即使⽤⼆级缓存

    <select useCache="false">
    </select>

    设置flushCache="true”属性,默认情况下为true,即刷新缓存,如果改成false则不会刷新

    <select  flushCache="true" useCache="false">
    </select>

    一般情况下我们应该在进行增删改操作后提交事务刷新缓存,所以我们直接使用默认的配置即可

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到