MyBatis笔记

一、MyBatis介绍

1.1、框架是什么

1.2、什么是MyBatis

1.3、为什么用MyBatis

1.4、MyBatis框架概述

二、MyBatis入门案例

2.1、配置pom文件,引入依赖

2.2、配置log4j.properties

2.3、创建一个实体类User

2.4、创建UserMapper接口

2.5、创建UserMapper.xml(映射文件)

2.6、配置mybatis-config.xml映射文件

2.7、测试

三、自定义MyBatis--了解(MyBatis运行原理)

3.1、MyBatis运行原理

3.2、添加utils工具类

3.3、Resources

3.4、SqlSessionFactoryBuilder

3.5、Configuration

3.6、MappedStatement

3.7、SqlSessionFactory

3.8、SqlSession

3.9、ProxyFactory

3.10、测试


一、MyBatis介绍

1.1、框架是什么

   在介绍MyBatis之前,我们首先要对框架进行一个理解。框架(Framework)它既是一个框子也是一个架子,框架具有一个约束和支撑的作用。它是对已经基础的代码进行了封装并提供相应的API,开发者使用框架即直接可调用封装好的api,这样可以提高工作效率和开发速度。

1.2、什么是MyBatis

   MyBatis前身是apache的ibatis,是一个封装了jdbc的持久层框架,使开发者只需关注sql语句。

1.3、为什么用MyBatis

   因为jdbc在使用中需要手动创建和释放连接,对结果的解析,sql语句硬编码在代码中。

1.4、MyBatis框架概述

   MyBatis是一款优秀的持久层框架,对jdbc的操作数据库过程进行封装,这样不需要花费精力去处理例如注册驱动、创建connection、手动设置参数、获取结果集等jdbc复杂的过程代码。

二、MyBatis入门案例

2.1、配置pom文件,引入依赖

  在初始案例中我们可以先引入一些必要的依赖比如mybatis、mysql-connection-java(数据库)、log4j(日志记录)、junit(测试)等。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.by</groupId>
    <artifactId>MyBatis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>proxy</module>
        <module>s2_MyBatis_Hello</module>
    </modules>

    <properties>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <build>
        <!-- 如果不添加此节点src/main/java目录下的所有配置文件都会被漏掉。 -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>

</project>

2.2、配置log4j.properties

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

2.3、创建一个实体类User

package com.by.pojo;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private  String address;

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

2.4、创建UserMapper接口

   建一个mapper包,在包下创建一个UserMapper接口,接口中写入一个查询方法

package com.by.mapper;

import com.by.pojo.User;

import java.util.List;

public interface UserMapper {
    public List<User> findAll();
}

2.5、创建UserMapper.xml(映射文件)

   我们可以将UserMapper.xml先创建在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接口下的操作
   id:Mapper接口的方法名
   resultType:Mapper接口方法的返回类型
-->
<mapper namespace="com.by.mapper.UserMapper">
    <select id="findAll" resultType="com.by.pojo.User">
        select * from user;
    </select>
</mapper>

2.6、配置mybatis-config.xml映射文件

在resources下创建mybatis-config.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">//引入config约束
<configuration>
      <!--默认操作dev-->
    <environments default="dev">
         <!--dev为开发环境-->
        <environment id="dev">
            <!--配置事务的类型:type="JDBC | MANAGED" 两种方式
                 JDBC:表示使用JDBC中原生的事务管理方式
                 MANAGED:被管理,例如Spring-->
            <transactionManager type="JDBC"></transactionManager>
              <!--配置数据库连接信息,用的是数据源(连接池)-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/whn1?useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
           <!--告知mybatis映射文件位置-->
        <mapper resource="com/by/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

2.7、测试

我们可以在src/test/java下创建一个test包,用于我们对配置好的mybatis进行测试

package com.by.test;

import com.by.mapper.UserMapper;
import com.by.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;


public class MyBatisTest {
    @Test
    public void findAll() throws IOException {
       //加载配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
        //获取会话实例
        SqlSession session = sqlSessionFactory.openSession();
        //通过session.getMapper调用代理得到userMapper
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<User> userList = userMapper.findAll();
        for ( User user:userList) {
            System.out.println(user);
        }
    }
}

测试结果

三、自定义MyBatis--了解(MyBatis运行原理)

3.1、MyBatis运行原理

   在上一章节测试中,我们引入了几个对象以保证mybatis可以正常通过测试,而这些对象也是mybatis运行时需要经历的几个步骤。我们可以通过写入自定义对象以了解其中的运行原理。

3.2、添加utils工具类

我们需要添加三个工具类:

1.XMLConfigBuilder:解析XML配置文件

package com.by.utils;

import com.by.cfg.Configuration;
import com.by.io.Resources;
import com.by.mapping.MappedStatement;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  用于解析配置文件
 */
public class XMLConfigBuilder {



    /**
     * 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方
     * 使用的技术:
     *      dom4j+xpath
     */
    public static Configuration loadConfiguration(InputStream config){
        try{
            //定义封装连接信息的配置对象(mybatis的配置对象)
            Configuration cfg = new Configuration();

            //1.获取SAXReader对象
            SAXReader reader = new SAXReader();
            //2.根据字节输入流获取Document对象
            Document document = reader.read(config);
            //3.获取根节点
            Element root = document.getRootElement();
            //4.使用xpath中选择指定节点的方式,获取所有property节点
            List<Element> propertyElements = root.selectNodes("//property");
            //5.遍历节点
            for(Element propertyElement : propertyElements){
                //判断节点是连接数据库的哪部分信息
                //取出name属性的值
                String name = propertyElement.attributeValue("name");
                if("driver".equals(name)){
                    //表示驱动
                    //获取property标签value属性的值
                    String driver = propertyElement.attributeValue("value");
                    cfg.setDriver(driver);
                }
                if("url".equals(name)){
                    //表示连接字符串
                    //获取property标签value属性的值
                    String url = propertyElement.attributeValue("value");
                    cfg.setUrl(url);
                }
                if("username".equals(name)){
                    //表示用户名
                    //获取property标签value属性的值
                    String username = propertyElement.attributeValue("value");
                    cfg.setUsername(username);
                }
                if("password".equals(name)){
                    //表示密码
                    //获取property标签value属性的值
                    String password = propertyElement.attributeValue("value");
                    cfg.setPassword(password);
                }
            }
            //取出mappers中的所有mapper标签,判断他们使用了resource还是class属性
            List<Element> mapperElements = root.selectNodes("//mappers/mapper");
            //遍历集合
            for(Element mapperElement : mapperElements){
                //判断mapperElement使用的是哪个属性
                Attribute attribute = mapperElement.attribute("resource");
                if(attribute != null){
                    System.out.println("使用的是XML");
                    //表示有resource属性,用的是XML
                    //取出属性的值
                    String mapperPath = attribute.getValue();//获取属性的值"com/by/dao/UserDao.xml"
                    //把映射配置文件的内容获取出来,封装成一个map
                    Map<String, MappedStatement> mappers = loadMapperConfiguration(mapperPath);
                    //给configuration中的mappers赋值
                    cfg.setMappers(mappers);
                }else{
                    System.out.println("使用的是注解");
                    //表示没有resource属性,用的是注解
                    //获取class属性的值
                    String daoClassPath = mapperElement.attributeValue("class");
                    //根据daoClassPath获取封装的必要信息
                    Map<String,MappedStatement> mappers = loadMapperAnnotation(daoClassPath);
                    //给configuration中的mappers赋值
                    cfg.setMappers(mappers);
                }
            }
            //返回Configuration
            return cfg;
        }catch(Exception e){
            throw new RuntimeException(e);
        }finally{
            try {
                config.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

    }

    /**
     * 根据传入的参数,解析XML,并且封装到Map中
     * @param mapperPath    映射配置文件的位置
     * @return  map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
     *          以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
     */
    private static Map<String,MappedStatement> loadMapperConfiguration(String mapperPath)throws IOException {
        InputStream in = null;
        try{
            //定义返回值对象
            Map<String,MappedStatement> mappers = new HashMap<String,MappedStatement>();
            //1.根据路径获取字节输入流
            in = Resources.getResourceAsStream(mapperPath);
            //2.根据字节输入流获取Document对象
            SAXReader reader = new SAXReader();
            Document document = reader.read(in);
            //3.获取根节点
            Element root = document.getRootElement();
            //4.获取根节点的namespace属性取值
            String namespace = root.attributeValue("namespace");//是组成map中key的部分
            //5.获取所有的select节点
            List<Element> selectElements = root.selectNodes("//select");
            //6.遍历select节点集合
            for(Element selectElement : selectElements){
                //取出id属性的值      组成map中key的部分
                String id = selectElement.attributeValue("id");
                //取出resultType属性的值  组成map中value的部分
                String resultType = selectElement.attributeValue("resultType");
                //取出文本内容            组成map中value的部分
                String queryString = selectElement.getText();
                //创建Key
                String key = namespace+"."+id;
                //创建Value
                MappedStatement mapper = new MappedStatement();
                mapper.setQueryString(queryString);
                mapper.setResultType(resultType);
                //把key和value存入mappers中
                mappers.put(key,mapper);
            }
            return mappers;
        }catch(Exception e){
            throw new RuntimeException(e);
        }finally{
            in.close();
        }
    }

    /**
     * 根据传入的参数,得到dao中所有被select注解标注的方法。
     * 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
     * @param daoClassPath
     * @return
     */
    private static Map<String,MappedStatement> loadMapperAnnotation(String daoClassPath)throws Exception{
        //定义返回值对象
        Map<String,MappedStatement> mappers = new HashMap<String, MappedStatement>();

        //1.得到dao接口的字节码对象
        Class daoClass = Class.forName(daoClassPath);
        //2.得到dao接口中的方法数组
        Method[] methods = daoClass.getMethods();
        //3.遍历Method数组
        for(Method method : methods){
            //取出每一个方法,判断是否有select注解
           /* boolean isAnnotated = method.isAnnotationPresent(Select.class);
            if(isAnnotated){
                //创建Mapper对象
                Mapper mapper = new Mapper();
                //取出注解的value属性值
                Select selectAnno = method.getAnnotation(Select.class);
                String queryString = selectAnno.value();
                mapper.setQueryString(queryString);
                //获取当前方法的返回值,还要求必须带有泛型信息
                Type type = method.getGenericReturnType();//List<User>
                //判断type是不是参数化的类型
                if(type instanceof ParameterizedType){
                    //强转
                    ParameterizedType ptype = (ParameterizedType)type;
                    //得到参数化类型中的实际类型参数
                    Type[] types = ptype.getActualTypeArguments();
                    //取出第一个
                    Class domainClass = (Class)types[0];
                    //获取domainClass的类名
                    String resultType = domainClass.getName();
                    //给Mapper赋值
                    mapper.setResultType(resultType);
                }
                //组装key的信息
                //获取方法的名称
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String key = className+"."+methodName;
                //给map赋值
                mappers.put(key,mapper);
            }*/
        }
        return mappers;
    }
}

2.DataSourceUtil:获取数据库连接对象

package com.by.utils;
import com.qf.cfg.Configuration;

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

/**
 * 用于创建数据源的工具类
 */
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.Executor:执行SQL,封装我们想要的数据

package com.by.utils;

import com.by.mapping.MappedStatement;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * 负责执行SQL语句,并且封装结果集
 */
public class Executor {

    public <E> List<E> selectList(MappedStatement mappedStatement, Connection conn) {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        try {
            //1.取出mapper中的数据
            String queryString = mappedStatement.getQueryString();//select * from user
            String resultType = mappedStatement.getResultType();//com.by.pojo.User
            Class domainClass = Class.forName(resultType);
            //2.获取PreparedStatement对象
            pstm = conn.prepareStatement(queryString);
            //3.执行SQL语句,获取结果集
            rs = pstm.executeQuery();
            //4.封装结果集
            List<E> list = new ArrayList<E>();//定义返回值
            while(rs.next()) {
                //实例化要封装的实体类对象
                E obj = (E)domainClass.newInstance();

                //取出结果集的元信息:ResultSetMetaData
                ResultSetMetaData rsmd = rs.getMetaData();
                //取出总列数
                int columnCount = rsmd.getColumnCount();
                //遍历总列数
                for (int i = 1; i <= columnCount; i++) {
                    //获取每列的名称,列名的序号是从1开始的
                    String columnName = rsmd.getColumnName(i);
                    //根据得到列名,获取每列的值
                    Object columnValue = rs.getObject(columnName);
                    //给obj赋值
                    PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
                    获取该属性的set方法
                    Method writeMethod = pd.getWriteMethod();
                    //调用set方法
                    writeMethod.invoke(obj,columnValue);
                }
                //把赋好值的对象加入到集合中
                list.add(obj);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            release(pstm,rs);
        }
    }


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

        if(pstm != null){
            try {
                pstm.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

3.3、Resources

在Resources类中我们可以通过类加载器加载配置文件,例如加载mybatis-config.xml,加载之后我们可以获取一个流。

package com.by.io;

import java.io.InputStream;

public class Resources {
    //根据文件名称,加载类路径下面的配置文件
    public static InputStream getResourceAsStream(String filePath){
           //加载配置文件,返回一个InputStream流
        return Resources.class.getClassLoader().getResourceAsStream(mybatis-config.xml);
    }
}

3.4、SqlSessionFactoryBuilder

 这是SqlSessionFactoryBuilder类,里面有一个build方法,build方法里有传流的参数,通过XMLConfigBuilder.loadConfiguration(in)将流放进去,这样会创建一个Configuration对象。这就是

 SqlSessionFactoryBuilder的作用。

package com.by.builder;

import com.by.cfg.Configuration;
import com.by.factory.DefaultSqlSessionFactory;
import com.by.factory.SqlSessionFactory;
import com.by.utils.XMLConfigBuilder;

import java.io.InputStream;

public class SqlSessionFactoryBuilder {

    /**
     * 构建SqlSessionFactory对象
     * @param in
     * @return
     */
    public SqlSessionFactory build(InputStream in){
        Configuration configuration = XMLConfigBuilder.loadConfiguration(in);
        return  new DefaultSqlSessionFactory(configuration);
    }
}

3.5、Configuration

配置类存储所有的配置信息

package com.by.cfg;

import com.by.mapping.MappedStatement;

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

public class Configuration {

    private String driver;
    private String url;
    private String username;
    private String password;
	
    /**
       Map<"com.by.dao.UserDao.findAll", MappedStatement>     
    */
    private Map<String, MappedStatement> mappers = new HashMap<String,MappedStatement>();

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

    public void setMappers(Map<String, MappedStatement> 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;
    }
}

3.6、MappedStatement

   MappedStatement是用来封装sql语句和查询结果集

package com.by.mapping;

public class MappedStatement {

    private String queryString;//SQL
    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.7、SqlSessionFactory

创建的configuration会传递给SqlSessionFactory这个工厂,SqlSessionFactory拿到configuration对象会创建一个SqlSession.

package com.by.factory;
public interface SqlSessionFactory {
    //获取SQLSession对象
    public SqlSession openSession();
}
package com.by.factory;

import com.by.cfg.Configuration;
import com.by.session.DefaultSqlSession;
import com.by.session.SqlSession;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private Configuration cfg;

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

    /**
     * 获取一个SqlSession对象
     * @return
     */
    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(cfg);
    }
}

3.8、SqlSession

通过SqlSessionFactory.openSession会创建一个Sqlsession会话,SqlSession会通过调用getMapper方法传递到ProxyFactory工厂,ProxyFactory通过反射获得MappedStatement(sql, resultType), conn。

public interface SqlSession {
    //获取代理对象
    public <T> T getMapper(Class<T> tClass);
     //释放资源
    void close();
}
package com.by.session;

import com.by.utils.DataSourceUtil;
import com.by.cfg.Configuration;

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

public class DefaultSqlSession implements SqlSession {

    private Configuration cfg;
    private Connection conn;

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


    /*
    * 创建代理对象
    */
    @Override
    public <T> T getMapper(Class<T> tClass) {
        /**
         * tClass.getClassLoader():类加载器
         * new Class[]{tClass}:Class数组,让代理对象和被代理对象有相同的行为
         *  new ProxyFactory:调用真是角色,附加自己的操作
         */
        return (T) Proxy.newProxyInstance(tClass.getClassLoader(),
                new Class[]{tClass},
                new ProxyFactory(cfg.getMappers(),conn));
    }

    @Override
    public void close() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3.9、ProxyFactory

ProxyFactory会解析得到MappedStatement(sql, resultType), conn数据,并会调用 Executor工具类查询

package com.by.session;

import com.by.mapping.MappedStatement;
import com.by.utils.Executor;

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

public class ProxyFactory implements InvocationHandler {

    private Map<String, MappedStatement> mappers;
    private Connection conn;
    public ProxyFactory(Map<String, MappedStatement> mappers, Connection conn){
       this.mappers = mappers;
       this.conn = conn;
    }
    //调用代理对象的任何方法,都会在这执行
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.获取方法名
        String methodName = method.getName();
        //2.获取方法所在类的名称
        String className = method.getDeclaringClass().getName();
        //3.组合key
        String key = className+"."+methodName;
        //4.获取mappers中的Mapper对象
        MappedStatement mappedStatement = mappers.get(key);
        //5.判断是否有mapper
        if(mappedStatement == null){
            throw new IllegalArgumentException("传入的参数有误");
        }
        //6.调用工具类执行查询所有
        return new Executor().selectList(mappedStatement,conn);
    }
}

3.10、测试

//1.读取配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.创建SqlSessionFactory工厂
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(in);
    System.out.println("-----" + factory);
    //3.使用工厂生产SqlSession对象
    SqlSession session = factory.openSession();
    //4.使用SqlSession创建Dao接口的代理对象
    UserDao userDao = session.getMapper(UserDao.class);
    //5.使用代理对象执行方法
    List<User> users = userDao.findAll();
    for(User user : users){
        System.out.println(user);
    }
    //6.释放资源
    session.close();
    in.close();

四、MyBatis基本的CRUD

4.1、查询

4.1.1、单个参数绑定

当查询语句需要一个传参条件时,#{value} value值是任意的

写入Mapper层

package com.by.mapper;
import com.by.pojo.User;

public interface UserMapper {
    User findByUserId(Integer id);
}

写Mapper.xml

<?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.by.mapper.UserMapper">
<!--
    resultType:接口返回类型的全类名;
    #{id}:#{id}:它代表占位符,相当于原来 jdbc 部分所学的?,都是用于替换实际的数据。
-->
<select id="findByUserId" resultType="com.by.pojo.User">
    select * from user where id=#{id};<!--当参数只有一个时,#{值是任意的}-->
</select>
</mapper>

..测试

4.1.2、MyBatis传入多个参数(重点)


1.通过序号传参:#{arg0},#{arg1},将需要查询的参数条件按顺序写入。

 Mapper层:比如通过id和username查询用户

package com.by.mapper;

import com.by.pojo.User;


public interface UserMapper {
    User findByIdAndUsername(Integer id,String username);}

Mapper.xml

<?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.by.mapper.UserMapper">

    <select id="findByIdAndUsername" resultType="com.by.pojo.User">
        select * from user where id=#{arg0} and username=#{arg1};<!--参数需要按顺序书写#{arg0},#{arg1},#{arg2}-->
    </select>
</mapper>

 2.通过注解绑定多个参数:(@Param("value") Integer id) 通过注解传递注解的#{value}值

Mapper层:

package com.by.mapper;

import com.by.pojo.User;
import org.apache.ibatis.annotations.Param;


public interface UserMapper {
      <!--("value")与查询的#{value}中value值要保持一致-->
    User findByIdAndUsername2(@Param("id") Integer id, @Param("username") String username);
}

Mapper.xml:

<?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.by.mapper.UserMapper">
     <!--查询的#{value}与("value")中的value值保持一致-->
    <select id="findByIdAndUsername2" resultType="com.by.pojo.User">
        select * from user where id=#{id} and username=#{username};
    </select>

3. 对象参数绑定:传入的参数为对象时 查询条件为 #{对象的属性名}。

Mapper层:

package com.by.mapper;

import com.by.pojo.User;

public interface UserMapper {
    User findUser(User user);
}

Mapper.xml:

<?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.by.mapper.UserMapper">
      
    <select id="findUser" parameterType="com.by.pojo.User" resultType="com.by.pojo.User">
        select * from user where id=#{id} and username=#{username};<!--传参为#{对象的属性名}-->
    </select>
</mapper>

4.Map传参:传入的参数为Map,查询条件为#{map的key}

Mapper层:

package com.by.mapper;
import com.by.pojo.User;
import java.util.Map;

public interface UserMapper {

    User findUserByMap(Map<String,Object> map);
}

Mapper.xml:

<?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.by.mapper.UserMapper">

    <select id="findUserByMap" parameterType="com.by.pojo.User" resultType="com.by.pojo.User">
        select * from user where id=#{id} and username=#{username};<!--#{map的key}-->
    </select>
</mapper>

 4.1.3、模糊查询

传递单个参数通过CONCAT('%',#{value},'%')或者'%${value}%',使用${} 括号中只能是value。

 Mapper层:

package com.by.mapper;
import com.by.pojo.User;
import java.util.List;

public interface UserMapper {

    List<User> findUserByName(String username);}

Mapper.xml:

<?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.by.mapper.UserMapper">

 <!--select * from user where username like CONCAT('%',#{username},'%');-->
    <select id="findUserByName" parameterType="string" resultType="com.by.pojo.User">
    select * from user where username like '%${value}%';<!--${}只能是value-->
    </select>
</mapper>

4.1.4、聚合函数查询

Mapper层:

package com.by.mapper;
import com.by.pojo.User;

public interface UserMapper {
   //聚合函数查询
public Integer getTotal();

}

Mapper.xml

<!--聚合函数查询-->
<select id="getTotal" resultType="int">
    SELECT COUNT(id) FROM user
</select>

4.1.5、sql注入

#{}可以防止sql注入,${}不可以

Mapper层:

package com.by.mapper;
import com.by.pojo.User;


public interface UserMapper {
    User login(User userParam);
}

Mapper.xml:

<?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.by.mapper.UserMapper">

    <select id="login" parameterType="com.by.pojo.User" resultType="com.by.pojo.User">
   <!-- select * from user where username =#{username} and password=#{password};-->
        select * from user where username ='${username}' and password='${password}';
    </select>
</mapper>

Test.java

package com.by.test;

import com.by.mapper.UserMapper;
import com.by.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;



public class MyBatisTest {
    private SqlSession session;
    private InputStream is;

    @Before
    public void createSqlSession() throws IOException{
        //加载配置文件
        is = Resources.getResourceAsStream("mybatis-config.xml");
        //创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
        //获取会话实例
        session = sqlSessionFactory.openSession();
    }
    
@Test
    public void login() throws IOException{
        UserMapper userMapper=session.getMapper(UserMapper.class);
        User userParam = new User();
       <!--
        输入"' #"会把查询语句中#后面的内容注释掉
-->
        userParam.setUsername("张三丰' #");
        userParam.setPassword("123");
        User user = userMapper.login(userParam);
        System.out.println(user);

    }

    @After
    public void closeSqlSession() throws IOException{
        session.close();
        is.close();
    }
}

4.2、#{}和${}的区别

  • 当传入单个类型参数:#{任意值},${value},${}括号中的值只能是value。
  • 类型转换:#{}可以进行自动类型转换,${}不可以
  • 底层:#{}底层为PreparedStatement,${}底层为Statement
  • sql注入:#{}可以防止sql注入,${}不可以。

4.3、删除

Mapper:

     void deleteUserById(Integer id);

Mapper.xml:

<delete id="deleteUserById">
    delete from user where id=#{id}
</delete>

 4.4、修改

Mapper:

void updateUser(User user);

Mapper.xml:

    <update id="updateUser"  parameterType="com.by.pojo.User" >
     update user set username=#{username},password=#{password},sex=#{sex},birthday=#{birthday},address=#{address}
        where id=#{id}
    </update>

4.5、添加 

Mapper:

  void addUser(User user);

Mapper.xml:

 <insert id="addUser" useGeneratedKeys="true" keyProperty="id" parameterType="com.by.pojo.User">
        insert into user(username,password,sex,birthday,address)
        values (#{username},#{password},#{sex},#{birthday},#{address})
    </insert>

五、MyBatis的ORM映射

5.1、什么是ORM映射

MyBatis只能自动维护库表”列名“与”属性名“相同时的对应关系,二者不同时无法自动ORM,如下:

5.2、列的别名

在SQL语句中通过as为查询字段添加列别名来匹配该查询类的属性名:

Mapper:

package com.by.mapper;
import com.by.pojo.Role;
import java.util.List;

public interface RoleMapper {
    List<Role>  findRole();
}

Mapper.xml:

<?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.by.mapper.RoleMapper">
    <select id="findRole" resultType="com.by.pojo.Role">
        select role_name as roleName,
               role_desc as roleDesc
        from role;
    </select>
</mapper>

5.3、结果映射

使用ResultMap标签手动映射,解决实体字段和数据表字段不一致的问题。

Mapper:

    List<Role>  findRole();

Mapper.xml

  <!--
      id:与select查询标签返回值保持一致
      type:映射实体类全类名
-->    
<resultMap id="findRoleResultMap" type="com.by.pojo.Role">
          <!--
            property:实体属性名
            column:数据库表字段名
        -->
        <id property="id" column="id" />
        <result property="roleName" column="role_name" />
        <result property="roleDesc" column="role_desc"/>

    </resultMap>
    <select id="findRole" resultMap="findRoleResultMap">
        select * from role ;
    </select>

六、MyBatis的配置文件

6.1、properties标签

1.我们一般会把数据库配置信息定义在一个独立的配置文件里面,比如db.properties:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/whn1?useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456

2.在mybatis-config.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>
      <!--引入properties配置文件-->      
    <properties resource="db.properties"></properties>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
            <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>
  
</configuration>

6.2、typeAliases标签

我们可以通过typeAliases标签为实体类定义别名,提高书写效率,在mybatis-config.xml中引入typeAliases

<?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>
    <properties resource="db.properties"></properties>
    
    <typeAliases>
        <!--批量起别名-->
        <package name="com.by.pojo"/>
    </typeAliases>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
        </environment>
    </environments>

</configuration>

使用别名

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.by.dao.UserDao">
    <!--使用别名 通过批量起别名引用时用小写-->
    <select id="findAll" resultType="user">
        select * from user
    </select>
</mapper>

6.3、Mappers标签

Mappers标签的作用是用来在核心配置文件里面引入映射文件,引入方式有如下两种:

使用mapper映射文件路径:

<mappers>
        <mapper resource="com/by/dao/UserDao.xml"/>
    </mappers>

使用mapper接口的包名批量引入:

    <mappers>
        <package name="com.by.dao"></package>
    </mappers>

七、MyBatis的关联查询

7.1、关联查询

1.实体间的关系(拥有、属于)

OneToOne:一对一关系(account ←→ user )

OneToMany:一对多关系(user←→ account )

ManyToMany:多对多关系(user←→ role )

2.什么是关联查询

当访问关系的一方时,如果需要查看与之关联的另一方数据,则必须使用表连接查询,将查询到的另一方数据保存至本方属性中

3.关联查询语法:

指定“一方”关系时(对象),使用<associaion javaType="">

指定“多方”关系时(集合),使用<collection ofType="">

7.2、一对一查询

需求:查询银行卡信息 通过关联查询用户信息

分析:一个银行卡只能有一个用户信息,所以从查询银行卡信息关联查询用户信息时一对一关系。我们通过查询银行卡id查询对应用户信息

Pojo:加入User类对象作为Account类的一个属性

package com.by.pojo;

public class Account {
      private   Integer id;
      private Integer uid;
      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 getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

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

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

Mapper

package com.by.mapper;

import com.by.pojo.Account;
import com.by.pojo.User;

import java.util.List;

public interface AccountMapper {

   Account findAccountById(Integer id);
}

 Mapper.xml

<?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.by.mapper.AccountMapper">
<!--结果映射-->
<resultMap id="findAccountByIdResultMap" type="com.by.pojo.Account">
    <id column="aid" property="id"/>
    <result column="uid" property="uid"/>
    <result column="money" property="money"/>
    <!--指定关系表数据的封装规则-->
    <association property="user" javaType="com.by.pojo.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="sex" column="sex"/>
    <result property="birthday" column="birthday"/>
    <result property="address" column="address"/>
    </association>
</resultMap>
    <select id="findAccountById" resultMap="findAccountByIdResultMap">
        select a.id aid,
                   a.uid uid,
                   a.money money,
                   u.*
            from account a left join user u on a.uid=u.id
        where a.id=#{id};
    </select>

</mapper>

7.3、一对多查询

需求:查询用户信息及其关联的银行卡信息

分析:一个用户可以拥有多个银行卡,因此用户和卡之间关联关系为一对多。

Pojo:在User类中加入accountList属性

package com.by.pojo;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private  String address;
    private String password;
    List<Account> accountList;
    List<Role> roleList;

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

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = 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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }

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

Mapper

package com.by.mapper;
import com.by.pojo.Account;
import com.by.pojo.User;
import java.util.List;

public interface UserMapper {
    List<Account> selectAccountByUserId(Integer id);
}

Mapper.xml

<?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.by.mapper.UserMapper">
 <!--结果映射User-->
    <resultMap id="selectAccountByUserIdResultMap" type="com.by.pojo.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
        <result property="address" column="address"/>
    <!--
      collection 是用于建立一对多集合属性的对应关系
      ofType:用于指定集合元素的数据类型

-->
        <collection property="accountList" ofType="com.by.pojo.Account">
                <id property="id" column="aid"/>
                <result property="uid" column="uid"/>
                <result property="money" column="money"/>
        </collection>
    </resultMap>

    <select id="selectAccountByUserId" resultMap="selectAccountByUserIdResultMap">
        select u.*,
                   a.id aid,
                   a.uid,
                   a.money
            from user u left join account a on u.id=a.uid
        where u.id=#{id}
    </select>
</mapper>

 7.4、多对多查询

需求:查询角色及角色赋予的用户信息。

分析:一个用户可以拥有多个角色,一个角色也可以赋予多个用户,用户和角色为双向的一对多关系,多对多关系我们看成是双向的一对多关系。

Pojo:加入Role角色类用于关联用户表,在User类加入roleList属性

package com.by.pojo;

public class Role {
   private Integer id;
   private  String roleName;
   private  String roleDesc;
public Role(){

}

    public Role(Integer id, String roleName, String roleDesc) {
        this.id = id;
        this.roleName = roleName;
        this.roleDesc = roleDesc;
    }

    public Integer getId() {
        return id;
    }

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

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

 Mapper

package com.by.mapper;

import com.by.pojo.Account;
import com.by.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> selectAll();
}

 Mapper.xml

<?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.by.mapper.UserMapper">

<resultMap id="selectAllResultMap" type="com.by.pojo.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="sex" column="sex"/>
    <result property="birthday" column="birthday"/>
    <result property="address" column="address"/>
    <collection property="roleList" ofType="com.by.pojo.Role">
        <id property="id" column="rid"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDesc" column="role_desc"/>
    </collection>
</resultMap>
    <select id="selectAll" resultMap="selectAllResultMap">
        select u.*,
               r.id rid,
               r.role_name,
               r.role_desc
         from user u left join user_role ur on u.id=ur.uid<!--关联多表查询用户和角色信息-->
         left join role r on ur.rid=r.id;
    </select>

</mapper>

八、MyBatis的延迟加载

8.1、什么是延迟加载

开启延迟加载后,在真正使用数据时会发起级联查询,不用的时候不用查。

一对一延迟加载:

Mapper:

package com.by.mapper;
import com.by.pojo.Account;
import com.by.pojo.User;
import java.util.List;

public interface AccountMapper {
   Account findAccountById(Integer id);
}

Mapper.xml

<?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.by.mapper.AccountMapper">

<resultMap id="findAccountByIdResultMap" type="com.by.pojo.Account">
    <id column="id" property="id"/>
    <result column="uid" property="uid"/>
    <result column="money" property="money"/>
<!--
          property:属性名
          select: 要调用的 select 映射的 id
          column : 传递给 select 映射的参数
          fetchType="lazy":懒加载,默认情况下是没有开启延迟加载的,局部配置
         -->
    <association property="user" javaType="com.by.pojo.User" select="com.by.mapper.UserMapper.getUserById" column="uid" fetchType="lazy">
    </association>
</resultMap>
    <select id="findAccountById" resultMap="findAccountByIdResultMap">
       select * from account where id=#{id}
    </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.by.mapper.UserMapper">
<select id="getUserById" resultType="com.by.pojo.User">
    select * from user where id=#{id}
</select>
</mapper>

一对多延迟加载:

Mapper:

package com.by.mapper;
import com.by.pojo.Account;
import com.by.pojo.User;
import java.util.List;

public interface UserMapper {
     User selectAccountByUserId(Integer id);
}

Mapper.xml

<?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.by.mapper.UserMapper">
   <resultMap id="selectAccountByUserIdResultMap" type="com.by.pojo.User">
       <id property="id" column="id"/>
       <result property="username" column="username"/>
       <result property="birthday" column="birthday"/>
       <result property="sex" column="sex"/>
       <result property="address" column="address"/>
       <result property="password" column="password"/>
<!--
          property:属性名
          select: 要调用的 select 映射的 id
          column : 传递给 select 映射的参数
          fetchType="lazy":懒加载,默认情况下是没有开启延迟加载的,局部配置
         -->
       <collection property="accountList"
                   ofType="com.by.pojo.Account"
                   select="com.by.mapper.AccountMapper.getAccountById"
                   column="id"
                   fetchType="lazy"/>
   </resultMap>

    <select id="selectAccountByUserId" resultMap="selectAccountByUserIdResultMap">
        select * from user where id=#{id}
    </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.by.mapper.AccountMapper">
<select id="getAccountById" resultType="com.by.pojo.Account">
    select * from account where uid=#{uid}
</select>
</mapper>

 多对多延迟加载:

 Mapper:

package com.by.mapper;
import com.by.pojo.Role;
import java.util.List;

public interface RoleMapper {
    List<Role> selectRole();
}

Mapper.xml:

<?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.by.mapper.RoleMapper">
    <resultMap id="selectRoleResultMap" type="com.by.pojo.Role">
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDesc" column="role_desc"/>
<!--
          property:属性名
          select: 要调用的 select 映射的 id
          column : 传递给 select 映射的参数
          fetchType="lazy":懒加载,默认情况下是没有开启延迟加载的,局部配置
         -->
        <collection property="userList"
                    ofType="com.by.pojo.User"
                    select="com.by.mapper.UserMapper.selectUserById"
                    column="id"
                    fetchType="lazy"/>
    </resultMap>

    <select id="selectRole" resultMap="selectRoleResultMap">
        select * from role;
    </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.by.mapper.UserMapper">
 <select id="selectUserById" resultType="com.by.pojo.User">
        select
            u.*
        from user u left join  user_role ur on u.id=ur.uid
        left join role r on r.id=ur.rid
        where rid=#{rid}
    </select>
</mapper>

8.2、全局开启懒加载

在mybatis-config.xml中全局懒加载配置

 <!-- 全局配置延迟加载策略 -->
    <settings>
        <!--  打开延迟加载的开关  -->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

九、MyBatis的动态SQL

9.1、什么是动态SQL

MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接称完整的SQL之后再执行,以达到SQL复用、简化编程的效果。

9.2、if标签

    List<User> findUser(User user);

Mapper.xml

<?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.by.mapper.UserMapper">
    <select id="findUser" resultType="com.by.pojo.User">
        select * from user where 1=1
        <if test="username!=null and username!=''">
            and username=#{username}
        </if>
        <if test="birthday!=null">
            and birthday=#{birthday}
        </if>
        <if test="sex!=null and sex!=''">
            and sex=#{sex}
        </if>
        <if test="address!=null and address!=''">
            and address=#{address}
        </if>
    </select>
</mapper>

9.3、where标签

为了简化上面where 1=1的条件拼装,我们可以使用where标签将if标签代码块包起来,将1=1条件去掉。

若查询条件的开头为 “AND” 或 “OR”,where 标签会将它们去除。

    List<User> findUser2(User user);

Mapper.xml

<select id="findUser2" resultType="com.by.pojo.User">
        select * from user
        <where>
            <if test="username!=null and username!=''">
                and username=#{username}
            </if>
            <if test="birthday!=null">
                and birthday=#{birthday}
            </if>
            <if test="sex!=null and sex!=''">
                and sex=#{sex}
            </if>
            <if test="address!=null and address!=''">
                and address=#{address}
            </if>
        </where>
    </select>

9.4、set标签

set标签用于动态包含需要更新的列,并会删掉额外的逗号

    void updateUserById(User user);

Mapper.xml

<update id="updateUserById" parameterType="com.by.pojo.User">
        update user
        <set>
            <if test="username!=null and username!=''">
                 username=#{username},
            </if>
            <if test="birthday!=null">
              birthday=#{birthday},
            </if>
            <if test="sex!=null and sex!=''">
            sex=#{sex},
            </if>
            <if test="address!=null and address!=''">
             address=#{address},
            </if>
        </set>
        where id=#{id}
    </update>

9.5、trim标签

trim标签可以代替where标签、set标签,可以添加前缀和后缀,去除多余的前缀和后缀。

void addUser(User user);

Mapper.xml

<insert id="addUser" parameterType="com.by.pojo.User">
        insert into user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null and username!=''">
                username,
            </if>
            <if test="birthday!=null ">
                birthday,
            </if>
            <if test="sex!=null and sex!=''">
                sex,
            </if>
            <if test="address!=null and address!=''">
               address,
            </if>
        </trim>
        <trim prefix="values(" suffix=")" suffixOverrides=",">
            <if test="username!=null and username!=''">
              #{username},
            </if>
            <if test="birthday!=null ">
               #{birthday},
            </if>
            <if test="sex!=null and sex!=''">
              #{sex},
            </if>
            <if test="address!=null and address!=''">
              #{address},
            </if>
        </trim>
    </insert>

9.6、foreach标签

foreach标签的常见使用场景是集合进行遍历

   void deleteById(@Param("ids") List<Integer> ids);

Mapper.xml

    <delete id="deleteById" parameterType="list">
        delete from user where id
    <!--
            collection:取值list、array、map、@Param("keyName")、对象的属性名
            item:循环取出的具体对象
            open:起始符
            separator:分隔符
            close:结束符
        -->
        <foreach collection="ids" item="id" separator="," open="in(" close=")">
                  #{id}
        </foreach>
    </delete>

test

  @Test
    public void deleteUserById(){
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<Integer> ids=new ArrayList<>();
        ids.add(51);
        ids.add(52);
        ids.add(53);
        userMapper.deleteById(ids);
        }

  • 37
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值