专题——mybatis框架

思维导图:在这里插入图片描述

1.入门

1.1框架

框架是软件开发的一套解决方案,不同的解决方案解决不同的问题,框架封装了很多细节,使开发者们以极简的方式实现功能,大大提高了开发效率,他是一个半成品,有各种组件,选用某种组件完成系统实现。

1.2持久层技术解决方案

JDBC技术(规范),其中spring的jdbcTemplete是spring对jdbc的简单封装,Apache的DBUtil是Apache对JDBC的简单封装,都属于帮助类,并不是框架,我们还是需要写很多东西。

1.3mybatis概述

是一个持久层框架,用java语言写的,封装了jdbc的细节,开发者只需要关注sql语句,而无需关注,注册驱动,创建连接等过程。它使用ORM(Object Relation Mapping)思想实现了对结果集的封装,ORM是对象关系映射,将数据库表和实现类对应起来,是我们操作实体类就可以操作数据库

2.搭建mybatis项目

2.1创建maven工程

注意 :将maven仓库改成本地的
在这里插入图片描述

2.2创建实体类

2.3导入坐标

    <!--以jar的方式-->
    <packaging>jar</packaging>

    <dependencies>
        <!--导入mybatis坐标-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.0</version>
        </dependency>

        <!--导入mysql坐标-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>

        <!--导入junit坐标-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>

    <!--找到配置文件插件-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

2.4创建持久层接口和方法

2.5创建主配置文件

<?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用来配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <!--jdbc配置信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--配置文件的映射 resource为xml方式,class为注解方式-->
        <mapper resource="com/test/dao/IUserMapper.xml"></mapper>
    </mappers>
</configuration>

2.6创建mapper文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--配置持久层接口的mapper,其中namespace为接口的全限定类名-->
<mapper namespace="com.test.dao.IUserMapper">
    <!--id对应的是持久层接口中的方法名,必须要加上resultType属性作为返回之类型-->
    <select id="findAll" resultType="com.test.domain.User">
        select id,username,birthday,sex,address from user;
    </select>
</mapper>

2.7创建测试类

//定义xml文件字符串
String resource = "SqlMapConfig.xml";
//1.加载配置文件
InputStream in = Resources.getResourceAsStream(resource);
//2.创建SqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder.build(in);
//3.打开工厂会话,创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.通过SqlSession对象创建代理对象
IUserMapper userMapper = SqlSession.getMapper(IUserMapper.class);
//5.调用方法
List<User> userList = userMapper.findAll();
for(User user : userList){
    
    
    System.out.println(user);
}
//6.关闭资源
sqlSession.close();
in.close();

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.8环境搭建的注意事项

1.在mybatis中把持久层和操作接口名称和映射文件叫做mapper,所以IUerDao和IUserMapper一样

2.mapper配置文件的结构要和持久层接口的包结构相同

3.映射文件的配置文件中的mapper标签中namespqce属性值必须是持久层接口的全限定类名

4.映射配置文件的操作配置,id属性的取值必须是dao接口的方法名。且必须有返回值类型resultType=“对应实体类的全限定类名”

5.在使用注解时要将mapper配置文件删掉,才不会报错

3.自定义mybaits

mybatis在使用代理dao的方式实现增删改查时做了什么事?

一.创建代理对象

二.在代理对象中调用selectList方法

自定义mybatis能通过入门案例看到的类

class Resources
  
class SqlSessionFactoryBuilder
    
interface SqlSessionFactory
    
interface SqlSession

3.1步骤一:创建基本工程添加依赖等,但是不添加mybatis依赖

3.2步骤二:创建mybatis.io.Resources类

package com.test.mybatis.io;

import java.io.InputStream;

/**
 * TODO:使用类加载器读取配置文件的类
 *
 * @author blp
 * @date 2020/8/13 18:47
 */
public class Resources {
    
    
    /**
     * 根据传入的参数获取一个字节输入流
     *
     * @param filePath
     * @return
     */
    public static InputStream getResourceAsStream(String filePath) {
    
    
        //下面这句执行了三步,第一步获取当前类的字节码,第二步,获取字节码的类加载器,第三步根据类加载器读取配置
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}

3.3步骤三:创建SqlSessionFactoryBuilder类,并编写build方法

package com.test.mybatis.sqlsession;

import java.io.InputStream;

/**
 * TODO:用于创建一个sqlsessionfactory对象
 *
 * @author blp
 * @date 2020/8/13 18:53
 */
public class SqlSessionFactoryBuilder {
    
    

    public SqlsessionFactory build(InputStream config){
    
    
        return null;
    }
}

3.4步骤四:创建sqlsessionFactory接口

package com.test.mybatis.sqlsession;

/**
 * TODO:
 *
 * @author blp
 * @date 2020/8/13 18:56
 */
public interface SqlSessionFactory {
    
    
    /**
     * 用于打开一个新的sqlSession对象
     * @return
     */
    SqlSession openSession();
}

3.5步骤五:创建sqlsession接口

package com.test.mybatis.sqlsession;

/**
 * TODO:自定义mybatis中和数据库交互的核心类,他里面可以创建dao接口的代理对象
 *
 * @author blp
 * @date 2020/8/13 18:59
 */
public interface SqlSession {
    
    
    /**
     * 根据参数创建一个代理对象
     * @param daoInterfaceClass dao的接口字节码而不是是实现类
     * @param <T>
     * @return
     */
    <T> T getMapper(Class<T> daoInterfaceClass);

    /**
     * 资源释放
     */
    void close();
}

3.6步骤六:删除配置文件中的mybaits url约束,创建一个包utils

然后在包中添加解析xml的工具类,使用的是dom4j和xpath技术

需要导的依赖有

      <!--导入dom4j坐标-->
    <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>

      <!--导入xpath坐标-->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>

3.7步骤七:创建解析xml工具类

package com.test.mybatis.utils;

import com.test.cfg.Configuration;
import com.test.cfg.Mapper;
import com.test.mybatis.io.Resources;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * TODO
 *
 * @author blp
 * @date 2020/8/13 19:07
 */
public class XMLConfigBuilder {
    
    

    public static Configuration loadConfiguration(InputStream config){
    
    
        Configuration cfg=new Configuration();

        try {
    
    
            SAXReader saxReader=new SAXReader();
            Document document=saxReader.read(config);
            Element root=document.getRootElement();
            List<Element> propertyElements=root.selectNodes("//property");
            for(Element element:propertyElements){
    
    
                String name=element.attributeValue("name");
                if("driver".equals(name)){
    
    
                    String driver=element.attributeValue("value");
                    cfg.setDriver(driver);
                }
                if("url".equals(name)){
    
    
                    String url=element.attributeValue("value");
                    cfg.setUrl(url);
                }
                if("username".equals(name)){
    
    
                    String username=element.attributeValue("value");
                    cfg.setUsername(username);
                }
                if("password".equals(name)) {
    
    
                    String password = element.attributeValue("value");
                    cfg.setPassword(password);
                }
            }
            List<Element> mapperElements=root.selectNodes("//mappers/mapper");
            for(Element mapperElement:mapperElements){
    
    
                Attribute attribute=mapperElement.attribute("resource");
                if(attribute!=null){
    
    
                    System.out.println("这是xml解析");
                    String mapperPath=attribute.getValue();
                    Map<String,Mapper> mappers=loadMapperConfiguration(mapperPath);
                    cfg.setMappers(mappers);
                }else{
    
    
//                    System.out.println("这是注解实现");
//                    String daoClassPath=mapperElement.attributeValue("class");
//                    Map<String,Mapper> mappers=loadMapperAnnoation(daoClassPath);
//                    cfg.setMappers(mappers);
                }
            }

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return cfg;
    }
    private static Map<String,Mapper> loadMapperConfiguration(String mapperPath){
    
    
        System.out.println("132131312");
        Map<String,Mapper> mappers=new HashMap<String,Mapper>();
        try {
    
    
            InputStream in= Resources.getResourceAsStream(mapperPath);
            SAXReader reader=new SAXReader();
            System.out.println(in);
            Document document=reader.read(in);
            Element root=document.getRootElement();
            System.out.println(root);
            String namespace=root.attributeValue("namespace");
            System.out.println(namespace);
            List<Element> selsctElements=root.selectNodes("//select");
            System.out.println(selsctElements.size());
            for(Element selectElement:selsctElements){
    
    
                String id=selectElement.attributeValue("id");
                String resultType=selectElement.attributeValue("resultType");
                String queryString=selectElement.getText();
                String key=namespace+"."+id;
                Mapper mapper=new Mapper();
                mapper.setQueryString(queryString);
                mapper.setResultType(resultType);
                mappers.put(key,mapper);
            }

        } catch (DocumentException e) {
    
    
            e.printStackTrace();
        }

        return mappers;
    }
}

3.8步骤八:创建自定义配置类和Mapper类(用于封装执行的sql语句和接口类型的全限定类名)

Configuration类

package com.test.cfg;

import java.util.HashMap;
import java.util.Map;

/**
 * TODO:自定义mybatis的配置类
 *
 * @author blp
 * @date 2020/8/13 19:25
 */
public class Configuration {
    
    

    private String driver;
    private String url;
    private String username;
    private String password;

    private Map<String,Mapper> mappers=new HashMap<String, Mapper>();

    public Map<String, Mapper> getMappers() {
    
    
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
    
    
        this.mappers.putAll(mappers);//此处需要使用追加的方式
    }

    public String getDriver() {
    
    
        return driver;
    }

    public void setDriver(String driver) {
    
    
        this.driver = driver;
    }

    public String getUrl() {
    
    
        return url;
    }

    public void setUrl(String url) {
    
    
        this.url = url;
    }

    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;
    }
}

Mapper类

package com.test.cfg;

/**
 * TODO:用于封装执行的sql语句和结果类型的全限定类名
 * String
 *
 * @author blp
 * @date 2020/8/13 19:36
 */
public class Mapper {
    
    
    //Sql语句
    private String queryString;
    //实体类的全限定类名
    private String resultType;

    public String getQueryString() {
    
    
        return queryString;
    }

    public void setQueryString(String queryString) {
    
    
        this.queryString = queryString;
    }

    public String getResultType() {
    
    
        return resultType;
    }

    public void setResultType(String resultType) {
    
    
        this.resultType = resultType;
    }
}

3.9步骤九:创建sqlsessionfactory的实现类

package com.test.mybatis.sqlsession;

import com.test.cfg.Configuration;

/**
 * TODO:SqlSessionFactory接口的是现类
 *
 * @author blp
 * @date 2020/8/13 21:04
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory{
    
    

    private Configuration cfg;
    public DefaultSqlSessionFactory(Configuration cfg){
    
    
        this.cfg=cfg;
    }
    /**
     * 用于创建一个新的数据库操作对象
     * @return
     */
    public SqlSession openSession(){
    
    
        return new DefaultSqlSession(cfg);
    }
}

3.10步骤十:创建sqlsession接口的实现类

package com.test.mybatis.sqlsession;

import com.test.cfg.Configuration;
import com.test.mybatis.sqlsession.proxy.MapperProxy;
import com.test.mybatis.utils.DataSourceUtil;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * TODO:SqlSession的实现类
 *
 * @author blp
 * @date 2020/8/13 21:09
 */
public class DefaultSqlSession implements SqlSession {
    
    
    private Configuration cfg;
    private Connection connection;

    public DefaultSqlSession(Configuration cfg) {
    
    
        this.cfg = cfg;
        connection = DataSourceUtil.getConnection(cfg);
    }

    /**
     * 用于创建代理对象
     *
     * @param daoInterfaceClass dao的接口字节码而不是是实现类
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> daoInterfaceClass) {
    
    
       return (T)Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(), new Class[]{
    
    daoInterfaceClass}, new MapperProxy(cfg.getMappers(), connection));
    }

    /**
     * 用于释放资源
     */
    public void close() {
    
    
        if (connection != null) {
    
    
            try {
    
    
                connection.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

3.11步骤十一:创建连接的工具类

package com.test.mybatis.utils;

import com.test.cfg.Configuration;

import java.sql.Connection;
import java.sql.DriverManager;

/**
 * TODO:用于创建数据源的工具类
 *
 * @author blp
 * @date 2020/8/13 21:44
 */
public class DataSourceUtil {
    
    
    /**
     * 用于获取一个链接
     * @param cfg
     * @return
     */
    public static Connection getConnection(Configuration cfg){
    
    
        try {
    
    
            Class.forName(cfg.getDriver());
            return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
        }catch(Exception e){
    
    
            throw new RuntimeException(e);
        }
    }
}

3.12步骤十二:创建代理对象的类

package com.test.mybatis.sqlsession.proxy;

import com.test.cfg.Mapper;
import com.test.mybatis.utils.Executor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Map;

/**
 * TODO
 *
 * @author blp
 * @date 2020/8/13 21:15
 */
public class MapperProxy implements InvocationHandler {
    
    
    private Map<String,Mapper> mappers;
    private Connection conn;

    public MapperProxy(Map<String,Mapper> mappers, Connection conn){
    
    
        this.mappers=mappers;
        this.conn=conn;
    }

    /**
     * 用于对方法进行增强,其实就是调用selectList方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //1.从map中找到mapper,获取方法名
        String methodName=method.getName();
        //2.获取方法所在类名称
        String className=method.getDeclaringClass().getName();
        //3.组合key
        String key=className+"."+methodName;
        System.out.println("______"+key);
        System.out.println(mappers);
        //4.获取mappers中的mapper对象
        Mapper mapper=mappers.get(key);
        //5.判断是否有mapper
        if(mapper==null){
    
    
            throw new IllegalArgumentException("参数有误");
        }
        //6.调用工具类查询所有
        return new Executor().selectList(mapper,conn);

    }
}

3.13步骤十三:创建查询工具类

package com.test.mybatis.utils;

import com.test.cfg.Mapper;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * TODO
 *
 * @author blp
 * @date 2020/8/13 21:24
 */
public class Executor {
    
    
    public <E> List<E> selectList(Mapper mapper, Connection conn){
    
    
        PreparedStatement pstm=null;
        ResultSet rs=null;
        List<E> list=new ArrayList<E>();
        try {
    
    
            String queryString=mapper.getQueryString();
            String resultType=mapper.getResultType();
            Class domainClass=Class.forName(resultType);
            pstm=conn.prepareStatement(queryString);
            rs=pstm.executeQuery();

            while(rs.next()){
    
    
                E obj=(E)domainClass.newInstance();
                ResultSetMetaData rsmd=rs.getMetaData();
                int columnCount=rsmd.getColumnCount();
                for(int i=1;i<=columnCount;i++){
    
    
                    String columnName=rsmd.getColumnName(i);
                    Object columnValue=rs.getObject(columnName);
                    PropertyDescriptor pd=new PropertyDescriptor(columnName,domainClass);
                    Method writeMethod=pd.getWriteMethod();
                    writeMethod.invoke(obj,columnValue);
                }
                list.add(obj);
            }

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }finally{
    
    
            release(pstm,rs);
        }
        return list;
    }

    public void release(PreparedStatement pstm,ResultSet rs){
    
    
        if(rs!=null) {
    
    
            try {
    
    
                rs.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if(pstm!=null){
    
    
            try {
    
    
                pstm.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }

        }

    }
}

3.14步骤十四:调用测试类进行测试即可

3.15 总结

sqlsessionbuilder接收sqlMapConfig.xml文件流创建sqlsessionfactory工厂-》该工厂读取到xml中的连接数据库和mapper映射信息,用来生产出真正操作数据库的sqlsession对象-》sqlsession有两个作用:1.生成接口的代理对象,2.定义通用增删改查方法-》封装结果集

4.mybatis的crud(基于代理dao的方式)

4.1添加

添加用户信息时配置mapper文件

<!--保存用户信息 id为方法名 parameterType为传入参数的返回值类型  #{username}为实体类中属性值-->
<insert id="saveUser" parameterType="com.itblp.domain.User">
INSERT INTO user (username,sex,address,birthday) VALUES (#{username},#{sex},#{address},#{birthday});
</insert>

编写测试类

class MybatisTest{
    
    
    
    public static finally String RESOURCE="SqlMapConfig.xml";
    //将创建inputstream,sqlsessionfactory,sqlsession,iuserdao提取出来,因为后后面要释放资源
    private InputStream in;
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;
    private IUserDao userDao;
    
    //创建一个初始化方法,用于初始化inputstream,sqlsessionfactory,sqlsession,iuserdao对像
    //@Before注解是在所有测试方法执行之前执行的
    @Before
    public void init() throws Exception{
    
    
        in = Resources.getResourceAsStream();
        sqlSessionFactory = new SqlSessionFactoryBuilder.build(in);
        sqlSession = sqlSessionFactory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    
    //创建一个释放资源的方法用于释放inputstream,sqlsession
    //@After注解实在所有测试方法执行之后执行的
    @After
    public void destroy() throws Exception{
    
    
        //此时添加一个手动提交,因为mybatis在执行添加方法时会关闭自动提交
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }
    
    //查询所有用户
    @Test
    public void testFindAll(){
    
    
        List<User> userList = userDao.findAll();
        for(User user:userList){
    
    
            System.out.println(user);
        }
    }
    
    //添加用户信息
    @Test
    public void testSaveUser(){
    
    
        User user = new User();
        user.setUserName("zhang");
        user.setAddress("xinjiang");
        user.setSex("男");
        user.setBirthday(new Date());
        userDao.saveUser(user);
        System.out.println(user.getId());//返回插入成功之后的id,后面有讲
        //这里需要有一个手动提交 sqlsession.commit()的方法,提取到公共方法中了,没有的话数据库添加不进去数据
    }
}

4.2删除

注意1:删除时如果时包装类型,并且是一个参数,占位符可以随便命名

<delete id=deleteUser parameterType="java.lang.Integer">//parameter的参数可以为INTEGER,INT和java.lang.Integer
    DELETE FROM USER WHERE id=#{uid};//其中在实体类中id为id,持久层接口中形参为userid
</delete>

4.3模糊查询

注意2:根据用户名进行模糊查询 有两种方法

方法一:

//dao
List<User> findByName(@Param("uname") String name);
//mapper.xml
<select>
select * from user where username like '%${uname}%';  //或者直接 like '%${value}%' 就不用在dao层使用Param注解了  
</select>

方法二:

//dao
List<User> findByName(String name);
//mapper.xml
<select>
select * from user where username like #{
    
    username};    
</select>
    
//但是在测试的时候传参数要带上% %
//例如:方法调用
userDao.findByName("%张%");

区别

${value}使用的是字符串的拼接。使用的是Statem字符串拼接

#{value}使用的是占位符。使用prepareStatement的参数占位符(主要用这种)

4.4插入之后返回插入后的id

需要使用selectKey标签

<!--保存用户信息 id为方法名 parameterType为传入参数的返回值类型  #{username}为实体类中属性值-->
<insert id="saveUser" parameterType="com.itblp.domain.User">
    <!--keyProperty:对应实体类中的属性 keyColumn:对应数据库中的id resultType:返回值类型 order:在什么时候执行,一般在插入之后执行,所以为AFTER-->
    <selectKey keyProperty="id" keyColumn="id" resultType="INT" order="AFTER">
        select last_insert_id();
    </selectKey>
INSERT INTO user (username,sex,address,birthday) VALUES (#{username},#{sex},#{address},#{birthday});
</insert>

5.mybatis中的参数深入和结果集的深入

5.1传递简单类型

5.2传递pojo对象,也就是javabean,实体类对象

mybatis使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称

ognl表达式:apache开发出来的,全称叫做Object Graphic Navigation Language 对象图导航语言,作用是通过对象中的取值方法来获取数据,在写法上把get给省略了。

比如:我们获取用户的名称,在类中的写法:user.getUserName();

​ orgn表达式的写法:user.username;

mybatis为什么可以直接写username,而不用user.呢?

因为在parameterType中已经提供了这个属性所属的类,所以不需要写对象名,直接写属性名

5.2将查询条件作为参数

应用在多个对象组成一个查询条件实现数据查询

首先创建一个查询条件的实体类

public class QueryVo{
    
    
    private User user;
    
     public User getUser() {
    
    
        return user;
    }

    public void setUser(User user) {
    
    
        this.user = user;
    }
}

在IUserDao中添加方法

List<User> findByVo(QueryVo queryVo);

mapper配置类

<select id="findByVo" parameterType="com.itblp.domain.QueryVo" resultType="com.itblp.domain.User">
    <!--这是传入的参数是QueryVo类型的,所以使用他里面的user属性,user里面又有username属性,所以使用user.username-->
    select * from user where username like #{user.username};
</select>

测试类

public void findByVo{
    
    
    QueryVo queryVo = new QueryVo();
    User user = new User();
    user.setUsername("%张%");
    queryVo.setUser(user);
    List<User> userList = userDao.findByVo(queryVo);
    for(User user1:userList){
    
    
        System.out.println(user1);
    }
}

5.3结果类型封装

输出pojo对象 此时pojo还是一个实体类

输出pojo列表

解决数据库列名和实体类中属性名不同

1.取别名,执行效率高

2.配置resultMap,需要单另解析一段xml,所以执行效率变低,开发效率会变高

<!--id:自定义名字,查询时用到  type:实体类全限定类名 -->
<resultMap id="userMap" type="com.itblp.domain.User">
    <!--主键的配置  property:实体类中的属性名  column:数据库中列名-->
    <id property="userId" column="id"></id>
    <!--非主键的配置-->
    <result property="userName" column="username"></result>
    <result property="userAddress" column="address"></result>
    <result property="userSex" column="sex"></result>
    <result property="userBirthday" column="birthday"></result>
</resultMap>

<!--在之后的查询中,就不用resultType了,使用resultMap-->
<select id="findAll" resultMap="userMap">
select * from user;
</select>

6.基于传统dao的方式(了解)

创建实现类,然后在实现类中创建sqlsessionfactory和sqlsession对象,调用sqlsession对象中的方法,例如查询所有的方法

public class UserDaoImpl implements IUserDao {
    
    
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;
    public static final String namespace="com.itblp.dao.IUserDao";

    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
    
    
        this.sqlSessionFactory = sqlSessionFactory;
        this.sqlSession=sqlSessionFactory.openSession();
    }


    public List<User> findAll() {
    
    
        //2.调用sqlsession中的selectList方法   参数就是可以获取到配置文件中的namespqce中的名称
        List<User> userList = sqlSession.selectList(namespace+".findAll");
        //3.释放资源
        sqlSession.close();
        return userList;
    }
}


//测试方法 中要将sqlsessionfactory传过去

prepareStatement对象的执行方法:

execute:能执行crud的任何sql语句,返回值是一个boolean类型,表示是否有结果集

executeQuery:只能执行select语句,无法执行增删改,执行结果封装的结果集resultSet对象

executeUpdate:只能执行cud,查询语句无法执行,返回值是影响数据库记录的行数

7.mybatis中的配置(主配置文件:SqlMapConfig.xml)

7.1properties标签

可以使用properties标签内部配置数据库信息:

<!--主配置文件中配置properties属性,可以在标签内部配置数据库的信息,也可以在属性引用外部配置文件信息-->
<properties>
    <property name="driver" value="com.jdbc.mysql.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8"></property>
    <property name="username" value="root"></property>
    <property name="password" value="123"></property>
</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>

使用properties标签引用外部配置文件信息

<!--resource属性是按照类路径的写法,必须存在于类路径下,如果放在resource文件夹根路径下,可以直接写文件名-->
<!-- url属性:按照url的写法来写地址,可以唯一标志一个资源的位置,写法是必须有协议/主机:端口/uri
    uri:可以在应用中唯一定位一个资源 例如:file:///C:/Users/think/Desktop/day02_eesy_01mybatiscrud/src/main/resources/jdbcConfig.properties-->
<properties resource="jdbcConfig.properties">
</properties>

<dataSource type="POOLED">
    <!--引用外部properties文件需要加上jdbc前缀-->
    <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>

外部properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123

7.2typeAliase

<!--在主配置文件中,使用typeAliases设置别名,只能设置domain中类的别名-->
<typeAliases>
    <!--type为实体类中的全限定类名,alias为别名,在mapper文件中不区分大小写-->
	<typeAlias type="com.itblp.domain.User" alias="user"></typeAlias>
    <!--当typeAlias属性过多时,可以注释上面一句,使用package标签,包中的类名就是别名,不区分大小写-->
    <package name="com.itblp.domain"></package>
</typeAliases>

7.3mapper

<!--在主配置文件中可以设置mapper标签,将mapper标签换为package标签,package定义持久层接口包名-->
<mappers>
<!--原先写法:使用mapper标签,resource或class属性-->
    <mapper resource="com.itblp.dao.IUserDao"></mapper>
<!--当mapper标签过多时,可以注释上面一句,使用package标签的name属性-->
    <package name="com.itblp.dao"></package>
</mappers>

8.mybatis中的连接池和事务控制(原理部分了解,应用部分会用)

8.1mybaits中连接池的使用及分析

1.连接池:在实际开发中我们都会使用连接池,因为会减少我们获取连接所消耗的时间
在这里插入图片描述
2.mybatis中的连接池:

提供了三种方式的配置:

  1. 配置的位置:

主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式,取值分别是

  • POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis有针对该规范的实现。从池中获取一个连接用,用完会返回池中
    在这里插入图片描述
    在这里插入图片描述

  • UNPOOLED:采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有使用池的思想。创建一个新的连接用

  • JNDI: 采用服务器提供的JNDI技术实现来获取dataSource对象,不同的服务器所能拿到的dataSource对象是不一样的

    注意: 如果不是web或maven的war工程,是不能使用的。我们实际开发中使用的是tomcat服务器,采用的连接池dbcp连接池

    8.2事务控制的分析

mybatis中的事务是通过sqlsession对象的commit方法和rollbaack实现事务的提交和回滚

在sqlSessionFactory.openSession(true)

可以将事务设置成自动提交,也就是在增删改之后不用写sqlsession.commit();

**注意:**在一个方法中提交多条数据,不能设置为自动提交

9.mysql中基于xml配置的动态sql语句使用(会用)

mappers配置文件中的几个标签

9.1if

条件语句

<!--test属性中的值和#{}中的一样,都是类中的属性值,区分大小写,此时没有学习where标签,先用1=1连接--> 
<select id="findByCondition" resultMap="userMap">
        select id,username,sex,address,birthday from user where 1=1 and
           <if test="userName!=null and userName!=''">
                username=#{userName}
            </if>
            <if test="userSex!=null and userSex!=''">
                sex=#{userSex}
            </if>
            <if test="userAddress!=null and userAddress!=''">
                address=#{userAddress};
            </if>
    </select>

9.2where

<!--注意:在test语句中注意and的写法,sql语句以外的区分大小写,以内不区分大小写-->    
<!--根据条件查询-->
    <select id="findByCondition" resultMap="userMap">
        select id,username,sex,address,birthday from user
        <where>
            <if test="userName!=null and userName!=''">
                username=#{userName}
            </if>
            <if test="userSex!=null and userSex!=''">
                sex=#{userSex}
            </if>
            <if test="userAddress!=null and userAddress!=''">
                address=#{userAddress};
            </if>
        </where>
    </select>

9.3foreach

有可能我们查询的id有好几个,例如 select * from user where id in(2,3,5,6);

1.此时我们可以在queryvo中添加一个id的list集合

class QueryVo{
    
    
    private List<Integer> ids;
    
    public List<Integer> getIds() {
    
    
        return ids;
    }

    public void setIds(List<Integer> ids) {
    
    
        this.ids = ids;
    }
}

2.在接口层写方法

List<User> findUserByIds(QueryVo vo);

3.mapper配置

<sql id="userSql">
select id, username,address,sex from user
</sql>

<!--此时domain层已经在主配置文件中重命名过了,可以直接使用别名 resultMap也已经定义-->
<select id="findUserByIds" parameterType="queryvo" resultMap="userMap">
<include refid="userSql"></include>
    <where>
    <if test="ids!=null and ids.size()>0">
        <!--collection:实体类集合引用 open:sql连接开始 close:sql连接结束 item:每一项定义为uid与#{}中变量一致 sporator:分隔符-->
        <foreach collection="ids" open="and id in (" close=")" item="uid" sporator=",">
        #{uid}
        </foreach>
        </if>
    </where>
</select>

4.测试(省略)

9.4sql

抽取重复的sql语句

<!--注意:sql语句后面不要加封号,因为到时候有where语句时连接会失败--> 
<sql id="userSql">
    select id,username,address,sex,birthday from user
 </sql>

  <!--resulttype表示我们要将结果封装到哪里-->
    <select id="findAll" resultMap="userMap">
        <include refid="userSql"></include>
    </select>

    <!--根据条件查询-->
    <select id="findByCondition" resultMap="userMap">
        <include refid="userSql"></include>
        <where>
            <if test="userName!=null">
                username=#{userName}
            </if>
            <if test="userSex!=null">
                sex=#{userSex}
            </if>
            <if test="userAddress!=null">
                address=#{userAddress};
            </if>
        </where>
    </select>

10.mybatis中的多表操作(掌握)

特例:如果拿出每一个订单,他都只能属于一个用户,所以mybatis把多对一看成了一对一

mybatis中的多表查询:

示例:用户和账户

一个用户可以有多个账户

一个账户只能属于一个用户(多个账户也可以属于一个用户)

步骤:

建立两张表:用户表和账户表 建立外键

两个实体类:用户和账户

3.建立两个配置文件 用户和账户

4.实现配置:

当我们查询用户时,可以同时得到用户下所包含的账户信息

当我们查询账户时,可以同时得到账户所属的用户信息。

10.1一对多

一个用户可以下多个订单,多个订单属于一个用户

一对多关系映射中,主表实体应该包含从表实体的集合引用

    private List<Account> accountList;

    public List<Account> getAccountList() {
    
    
        return accountList;
    }

    public void setAccountList(List<Account> accountList) {
    
    
        this.accountList = accountList;
    }

在配置中,需要在resultMap标签中添加collection标签

<!--定义user的resultmap-->
    <resultMap id="userMap" type="user">
        <!--主键字段对应 property:  column:-->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
        <!--配置user对象中account集合的映射 ofType:指的是集合中元素的类型-->
        <!--property属性是实体类中集合的名称,ofType是集合中每一项的类型,因为此时设置了别名,所以可以直接使用account-->
        <collection property="accountList" ofType="account">
            <!--注意:此时id需要改名字,否则mybatis分不清是哪个id了-->
            <id property="id" column="aid"></id>
            <result property="userId" column="user_id"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

10.2多对一

订单和用户

示例:一个账户对应一个用户

1.在账户实体类中添加user实体的引用

public class Account{
    
    
    private Integer id;
    private Integer userId;
    private Double money;
    private User user;
    
   
    public User getUser() {
    
    
        return user;
    }

    public void setUser(User user) {
    
    
        this.user = user;
    }

    public Integer getId() {
    
    
        return id;
    }

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

    public Integer getUserId() {
    
    
        return userId;
    }

    public void setUserId(Integer userId) {
    
    
        this.userId = userId;
    }

    public Double getMoney() {
    
    
        return money;
    }

    public void setMoney(Double money) {
    
    
        this.money = money;
    }

    @Override
    public String toString() {
    
    
        return "Account{" +
                "id=" + id +
                ", userId=" + userId +
                ", money=" + money +
                '}';
    }
}

2.编写接口层方法

List<Account> findAll();

3.编写mapper配置文件

<resultMap id="accountUserMap" type="account">
	<id property="id" column="aid"></id>
    <result property="userId" column="user_id"></result>
    <result property="money" column="money"></result>
    <!--建立一对一的映射关系,配置封装user信息-->
    <association property="user" column="user_id" javaType="user">
        <id property="userId" column="id"></id>
            <result property="userName" column="username"></result>
            <result property="userAddress" column="address"></result>
            <result property="userSex" column="sex"></result>
            <result property="userBirthday" column="birthday"></result>
    </association>
</resultMap>

4.测试类

@Test
public void testFindAll(){
    
    
    List<Account> accountList = accountDao.findAll();
    for(Account account:accountList){
    
    
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

10.3一对一

一个人只能有一个身份证号,一个身份证号只能属于一个人

10.4多对多

一个学生可以被多个老师教

一个老师可以教多个学生

示例:用户和角色

​ 用户可以有多个角色

​ 角色可以赋予多个用户

步骤:

1.建立两张表:用户表和角色表

需要使用中间表,中间表中需要包含各自的主键

2.两个实体类:用户和角色

各自包含对方一个集合引用

3.建立两个配置文件 用户和角色

4.实现配置:使用的还是collection标签

当我们查询用户时,可以同时得到用户下所包含的角色信息

<!--确定该方法是在那个dao接口-->
<mapper namespace="com.itblp.dao.IUserDao">

    <!--定义user的resultmap-->
    <resultMap id="userMap" type="user">
        <!--主键字段对应 property:  column:-->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
        <collection property="roleList"  ofType="role">
            <id property="roleId" column="rid"></id>
            <result property="roleName" column="role_name"></result>
            <result property="roleDesc" column="role_desc"></result>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userMap">
       select u.*,r.id as rid,r.role_name,r.role_desc from user u LEFT join user_role ur on ur.uid=u.id LEFT JOIN role r on r.id=ur.rid
    </select>

</mapper>

当我们查询角色时,可以同时得到角色的所赋予的用户信息。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itblp.dao.IRoleDao">
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="userList" ofType="user">
            <id property="userId" column="id"></id>
            <result property="userName" column="username"></result>
            <result property="userAddress" column="address"></result>
            <result property="userSex" column="sex"></result>
            <result property="userBirthday" column="birthday"></result>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="roleMap">
        select r.id as rid,r.role_name,r.role_desc,u.* from role r LEFT JOIN user_role ru on ru.rid=r.id LEFT JOIN user u on u.id=ru.uid
    </select>
</mapper>

11.mybatis中的延迟加载

问题:在一对多中,当我们有一个用户,他有一百个账户。

在查询用户的时候,要不要把关联的账户查出来?
在查询用户时,用户下的账户信息应该是什么时候使用什么时候查询的。
在查询账户的时候,要不要把关联的用户查出来?
查询账户时,账户所属的用户信息应该一起查询出来。

11.1什么是延迟加载

在真正使用数据时,才发起查询,不用的时候不查询,也叫按需加载,懒加载

在一对一中实现延迟加载 即查询账户的时候懒加载用户信息

首先修改account的mapper配置文件

<!--添加一个select属性,值为IUserDao的全限定类名加根据id获取用户信息,此时必须要有column属性,根据id获取用户信息就是通过这个id-->
<association property="user" column="user_id" javaType="user" select="com.itblp.dao.IUserDao.getUserById">
	 <id property="userId" column="id"></id>
            <!--非主键字段的对应-->
            <result property="userName" column="username"></result>
            <result property="userAddress" column="address"></result>
            <result property="userSex" column="sex"></result>
            <result property="userBirthday" column="birthday"></result>
</association>

<select>
select * from account
</select>

修改主配置文件

<settings>
    <!--开启延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"></setting>
    <!--允许触发方法进行延迟加载-->
<setting name="aggressiveLazyLoading" value="true"></setting>
</settings>

在一对多中实现延迟加载,和一对一其实很相似

mapper配置文件

   <resultMap id="userMap" type="user">
        <!--主键字段对应 property:  column:-->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
        <!--配置user对象中account集合的映射 ofType:指的是集合中元素的类型 select为account接口中的fandAccountbyId方法-->
        <collection property="accountList" ofType="account" column="id" select="com.itblp.dao.IAccountDao.findAccountByUId">
            <id property="id" column="id"></id>
            <result property="userId" column="user_id"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

11.2什么是立即加载

不管用不用,只要一调用方法,马上发起查询。

在对应的四种关系表中:一对一,多对一,一对一,多对多
一对多,多对多:通常情况下使用延迟加载。
多对一,一对一:通常情况下采用立即加载

12.mybatis中的缓存

12.1什么是缓存

存在内存中的临时数据

12.2为什么使用缓存

减少和数据库的交互次数,提高执行效率

什么样的数据能使用缓存,什么样的数据不能使用

经常查询的数据,并且不经常改变的,数据的正确与否队最终结果影响不大的适用于缓存。

经常改变的数据,数据的正确与否对最终结果影响很大,例如:商品的库存,银行的汇率

12.3mybatis中的一级缓存和二级缓存

一级缓存:指的是mybatis中sqlSession对象的缓存。

当我们执行查询之后,查询的结果会同时存入到sqlSession为我们提供的一块区域中,该区域的结构是一个map。当我们再次查询同样的数据,mybatis会先去sqSession中查看是否有,有的话直接拿出来用,Sqlsession对象消失时,mybatis的一级缓存也就消失了

测试:

//测试mybatis的一级缓存
@Test
public void testFirstCache(){
    
    
    User user1 = userDao.getUserById(2);
    System.out.println(user1);
    User user2 = userDao.getUserById(2);
    System.out.println(user2);
    System.out.pringln(user1 == user2);
    //com.itblp.domain.User@1e965684
    //com.itblp.domain.User@1e965684
    //true
    //两个对象是同一个对象,并且后台显示执行这个方法只查询了一次数据库,为了证明sqlsession对象的缓存就是mybatis的一级缓存
    //我们将sqlsession对象在第一次使用后关闭,然后再第二次使用前重新创建一个
}
//将sqlsession对象在第一次使用后关闭,然后再第二次使用前重新创建一个
@Test
public void testFirstCache(){
    
    
    User user1 = userDao.getUserById(2);
    System.out.println(user1);
    sqlSession.close();
    sqlSession = sqlSessionFactory.openSession();
    userDao = sqlSession.getMapper(IUserDao.class);
    User user2 = userDao.getUserById(2);
    System.out.println(user2);
    System.out.println(user1 == user2);
    //com.itblp.domain.User@1e965684
    //com.itblp.domain.User@53f65459
    //false
    //可以看到是不同的对象,并且后台发起了两次查询
}

除了使用sqlsession.close()清除缓存,还可以使用sqlsession.clearCache()来清除缓存

//使用sqlSession中的clearCache来清除缓存
@Test
public void testFirstCache(){
    
    
    User user1 = userDao.getUserById(2);
    System.out.println(user1);
    sqlSession.clearCache();
    userDao = sqlSession.getMapper(IUserDao.class);
    User user2 = userDao.getUserById(2);
    System.out.println(user2);
    System.out.println(user1 == user2);
    //com.itblp.domain.User@1e965684
    //com.itblp.domain.User@53f65459
    //false
    //可以看到是不同的对象,并且后台发起了两次查询
}

mybatis如何保持缓存与再次查询数据库中数据的一致性

//mybatis规定在执行了增加,删除,修改,close,commit等操作之后都会将sqlSession销毁,再次使用查询时再重新创建,重新去数据库中查询
//测试一级缓存的同步性
@Test
public void testClearCache(){
    
    
    User user1 = userDao.getUserById(2);
    System.out.println(user1);
    user1.setUserName("清华");
    user1.setAddress("北京");
    userDao.updateUser(user1);
    User user2 = userDao.getUserById(2);
    System.out.println(user2);
    System.out.println(user1 == user2);
    //com.itblp.domain.User@1e965684
    //com.itblp.domain.User@3b088d51
    //false
}

**二级缓存:**它指的是mybatis中的sqlSessionFactory对象的缓存,有同一个sqlsessionfactory对象创建的sqlsession共享其缓存

二级缓存中存放的是数据,而不是对象,如果有新的sqlsession对象要使用这个数据,sqlSessionfactory就从缓存中把数据取出来给这个对象。

在这里插入图片描述
二级缓存的使用步骤:

第一步:让mybatis框架支持二级缓存,(在SqlMapConfig.xml中配置)

<settings>
<setting name="cacheEnabled" value="true"></setting>
</settings>

第二部:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

<!--mapper标签中添加-->
<cache/>

第三步:让当前的操作支持二级缓存(在select标签中配置)

<select id="getUserById" parameterType="int" resultMap="userMap" cache="true"></select>

13.mybatis中的注解开发

13.1环境搭建

和使用xml配置的环境 搭建是一样的操作
在这里插入图片描述

13.2单表CRUD操作(代理dao方式)

在单表CRUD操作时,使用sql拼接时需要注意

//根据用户姓名模糊查询用户信息,使用sql占位符
@Select("select id,username,address,sex,birthday from user where username like #{username}")
List<User> findUserByName(String name);

//使用sql拼接,使用默认的value传值,方法中的name不用改变
@Select("select id,username,address,sex,birthday from user where username like '%${value}%'")
List<User> findUserByName(String name);

//使用sql拼接,但是不是用value,则方法中的参数要设置Param注解
@Select("select id,username,address,sex,birthday from user where username like '%${username}'")
List<User> findUserByName(@Param("username") String name);

当实体类中属性和mysql中列名不对应时

@Select("select id,username,address,sex,birthday from user")
//Results注解解决数据库列名和类中属性名不一致的情况,id为唯一标识,为的是之后方法也可以使用,value属性是字段对应
@Results(id="userMap",value={
    
    
    //@Result为字段对应,id=true表示这是唯一主键
    @Result(id=true,property="userId",column="id"),
    @Result(property="userName",column="username"),
    @Result(property="userAddress",column="address"),
    @Result(property="userSex",column="sex"),
    @Result(property="userBirthday",column="birthday")
})
List<User> findAll();

@Select("select id,address,sex,username,birthday from user where id=#{id}")
//@使用ResultMap注解引用Results注解,可以使字段进行对应
@ResultMap(value={
    
    "userMap"})
User findUserById(Intger id);

13.3多表查询操作

多对一和一对一

1.在Account类中创建user类的引用

public class Account{
    
    
    //创建一对多关系映射,即一对一关系映射,在从类中创建主类的实体类引用
    private User user;
    public void setUser(User user){
    
    
        this.user=user;
    }
    public User getUser(){
    
    
        return this.user;
    }
}

2.在IAccountDao中添加注解

public interface IAccountDao{
    
    
    @Select("select * from account")
    @Results(id="accountMap",value={
    
    
        @Result(id=true,property="id",column="id"),
        @Result(property="userId",column="user_id"),
        @Result(property="money",column="money")
        //建立多堆一和一对一的关系映射,@Result注解中有@Result注解,Result注解中有one注解,这个注解就是解决多对一关系的
        //select属性是IUserDao中的根据id获取用户信息,column属性决定根据哪一列进行加载,fetchType加载类型选择的是懒加载,需要时才加载
        @Result(property="user",column="user_id",one=@One(select="com.itblp.dao.IUserDao.getUserById",
                                                          fetchType=FetchType.LAZY))
    })
    List<Account> findAll();
}

一对多

1.其实和多对一一样,user实体类中添加AccountList集合引用

public class User{
    
    
//一对多关系映射
    private List<Account> accountList;
        public List<Account> getAccountList() {
    
    
        return accountList;
    }

    public void setAccountList(List<Account> accountList) {
    
    
        this.accountList = accountList;
    }
}

2.在IUserDao中配置注解

public interface IUserDao{
    
    
    @Select("select id,username,address,sex,birthday from user")
    @Results(id="userMap",value={
    
    
        @Result(id=true,property="userId",column="id"),
        @Result(property="userName",column="username"),
        @Result(property="userAddress",column="address"),
        @Result(property="userSex",column="sex"),
        @Result(property="userBirthday",column="birthday"),
        //select为根据id查找account的方法的全限定名,id为根据userid进行查找
        @Result(property="accountList",column="id",
                many=@Many(select="com.itblp.dao.IAccountDao.findAccountById",fetchType=FetchType.LAZY))
        List<User> findAll();
    })
}

3.IAccountDao中需要定义根据UserId查找所有的账户信息

public interface IAccountDao{
    
    
    @Select("select a.id as aid,a.user_id,a.money from account where user_id=#{userId}")
    List<Account> findAccountById(Integer id);
}

13.4缓存的配置

测试一级缓存

@Test
public void testFindUserById(){
    
    
    User user=userDao.findUserById(29);
    Sysetm.out.println(user);
    
    User user1=userDao.findUserById(29);
    System.out.println(user1);
    System.out.println(user==user1);
    //一级缓存就是sqlSession对象的缓存,sqlsession对象没有被关闭,所有只在数据库中查询一次,并且是同一个对象
    //com.itblp.domain.User_$$_jvst2c8_0@6b9651f3
    //com.itblp.domain.User_$$_jvst2c8_0@6b9651f3
    //true
}

测试二级缓存

@Test
public void testSecondCache(){
    
    
    SqlSession sqlSession = sqlSessionFactory.openSession();
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);
    User user = userDao.findUserById(29);
    System.out.println(user);
    //关闭此sqlSession对象
    sqlSession.close();
    
    //重新创建sqlSession对象
    SqlSession sqlSession1=sqlSessionFactory.openSession();
    IUserDao userDao1=sqlSession1.getMapper(IUserDao.class);
    User user1 = userDao1.findUserById(29);
    System.out.println(user1);
    System.out.println(user == user1);
    //在没有配置二级缓存之前user和user为不同的对象,并且向数据库中查询两次数据
    //接下来进行二级缓存的配置
}

配置二级缓存

主配置文件中开启缓存

<settings>
<setting name="cacheEnabled" value="true"></setting>
</settings>

接口中配置

@CacheNamespace(blocking=true)	
public interface IUserDao{
    
    
    
}

配置完成后再测试二级缓存,会发现还是会创建两个对象,但是只会去数据库中查询一次,因为二级缓存共享数据,而不是共享对象。

猜你喜欢

转载自blog.csdn.net/weixin_42563224/article/details/108129672
今日推荐