11-SSM_MyBatis(上)

目录

一,Mybatis介绍

二,Mybatis解决的JDBC中的问题

1,原始的JDBC编写方法

2,Mybatis解决的问题 

三,Mybatis入门案例

1,案例1——mybatis使用流程

2,案例2——根据ID查询单个对象

3,案例3——添加、修改、删除

四,日志的使用

五,Mybatis对象分析&架构

1,对象分析

1.1 Resources

1.2 SqlSessionFactoryBuilder

1.3 sqlSessionFactory

1.4 SqlSession

2,架构

六,使用原有的DAO方式开发

七,使用ThreadLocal优化工具类

1,ThreadLocal介绍

2,使用ThreadLocal优化工具类

八,使用Mapper动态代理

1,什么是Mapper接口

2,实现步骤

3,实现原理


一,Mybatis介绍

MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为MyBatis 。2013 年 11 月迁移到 Github。iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。

iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAO)。

Mybatis 基于java的持久层框架,它的内部封装了JDBC,让开发人员只需要关注SQL语句本身,不需要花费精力在驱动的加载、连接的创建、Statement的创建等复杂的过程

Mybatis通过XML或注解的方式将要执行的各种的statement配置起来,并通过java对象和statement中的sql的动态参数进行映射生成最终执行的SQL语句,最后由mybatis框架执行SQL,并将结果直接映射为java对象。

采用了ORM思想解决了实体类和数据库表映射的问题。对JDBC进行了封装,屏蔽了JDBC API底层的访问细节,避免我们与jdbc的api打交道,就能完成对数据的持久化操作。

O- Object java对象 
R- Relation 关系,就是数据库中的一张表 
M- Mapping 映射

二,Mybatis解决的JDBC中的问题

1,原始的JDBC编写方法

public class TestJDBC { 
    public static void main(String[] args) { 
        Connection conn = null; 
        PreparedStatement ps = null; 
        ResultSet rs = null; 
        try {
            //加载驱动 ,这个不需要硬背。可以先写下Driver,看看他的依赖包提示,然后再添加进来
            Class.forName("com.mysql.cj.jdbc.Driver"); 
            String url="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT"; 
            //获取连接 
            conn= DriverManager.getConnection(url,"root","root"); 
            //SQL语句 
            String sql="select * from team;"; 
            ps=conn.prepareStatement(sql); 
            //执行查询 
            rs = ps.executeQuery(); 
            //遍历结果集 
            List<Team> list=new ArrayList<>(); 
            while (rs.next()){ 
                Team team=new Team(); 
                team.setTeamName(rs.getString("teamName")); 
                team.setTeamId(rs.getInt("teamId")); 
                team.setCreateTime(rs.getDate("createTime")); 
                team.setLocation(rs.getString("location")); 
                list.add(team); 
            }
            list.forEach(team -> System.out.println(team)); 
        }catch (Exception e){ 
            e.printStackTrace(); 
        }finally { 
            try {
                //关闭资源 
                if (rs != null){ 
                    rs.close(); 
                }
                if (ps != null){ 
                    ps.close(); 
                }
                if (conn != null){ 
                    conn.close(); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } 
        } 
    } 
}

2,Mybatis解决的问题 

  • 1、数据库连接的创建、释放连接的频繁操作造成资源的浪费从而影响系统的性能。
  • 2、SQL语句编写在代码中,硬编码造成代码不容易维护,实际应用中SQL语句变化的可能性比较大,一旦变动就需要改变java类。
  • 3、使用preparedStatement的时候传递参数使用占位符,也存在硬编码,因为SQL语句变化,必须修改源码。
  • 4、对结果集的解析中也存在硬编码。

三,Mybatis入门案例

1,案例1——mybatis使用流程

1,创建maven项目,在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.xxy</groupId>
    <artifactId>MybatisTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

2,在java/resources中添加mybatis.xml

一般情况下:配置文件的名称可以自定义,这里中使用mybatis.xml。配置文件放置在java/resources中。

头文件去官网中复制粘贴。在这里给大家提供一个中文的网站。Mybatis网址

  

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!--配置 mybatis环境-->
    <environments default="development">
        <!--id:数据源的名称-->
        <environment id="development">
            <!--事务类型:使用JDBC事务,使用Connection的提交和回滚-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源datasource:创建数据库的connection对象-->
            <!--type:POOLED 使用数据库的连接池-->
            <dataSource type="POOLED">
                <!--连接数据库的四大参数:driver、url、username、password-->
                <!--注意!这里使用的是MySQL8,如果是MySQL15的话,driver和url都不一样-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="2017217905"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

3,创建数据库和表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for team
-- ----------------------------
DROP TABLE IF EXISTS `team`;
CREATE TABLE `team`  (
  `teamId` int(11) NOT NULL AUTO_INCREMENT,
  `teamName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `location` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `createTime` date NULL DEFAULT NULL,
  PRIMARY KEY (`teamId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

4,编写实体类

package com.xxy.pojo;

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

public class Team implements Serializable {
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTime;

    @Override
    public String toString() {
        return "Team{" +
                "teamId=" + teamId +
                ", teamName='" + teamName + '\'' +
                ", location='" + location + '\'' +
                ", createTime=" + createTime +
                '}';
    }

    public Integer getTeamId() {
        return teamId;
    }

    public void setTeamId(Integer teamId) {
        this.teamId = teamId;
    }

    public String getTeamName() {
        return teamName;
    }

    public void setTeamName(String teamName) {
        this.teamName = teamName;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

5,编写ORM映射文件

针对实体类Team.java和表Team进行ORM映射.

Mybatis框架中,ORM映射是针对SQL语句进行,Mybatis框架将SQL语句抽取到了XML中,避免硬编码。所以我们需要针对每个实体类编写XML映射文件。

头文件同样在网站复制即可。

 mybatis3.3.1版本之前,映射文件中sql语句中加分号会报错!

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.xxy.pojo.Team">
    <!-- id="自定义名称,id不能重复;相当于dao中的方法名称"
    resultType="使用的要求:实体类中的属性名与表中的列名一致" -->
    <select id="queryAll" resultType="com.xxy.pojo.Team">
        select * from team;
    </select>
</mapper>

 6,将映射文件注册到mybatis的配置文件中

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!--配置 mybatis环境-->
    <environments default="development">
        ...
    </environments>
    <!--注册映射文件-->
    <mappers>
        <mapper resource="com/xxy/pojo/Team.xml"></mapper>
    </mappers>
</configuration>

7,在pom.xml中配置映射文件的扫描位置

    <build>
        <resources>
            <resource>
                <!--所在的目录-->
                <directory>src/main/java</directory>
                <includes>
                    <!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        ...
    </build>

8,编写测试类

package com.xxy.test;

import com.xxy.pojo.Team;
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.Reader;
import java.util.List;

public class TeamTest {
    // mybatis的配置文件--相当于创建工厂的图纸
    private String resource = "mybatis.xml";
    @Test
    public void test01() {
        try {

            // 1,读取mybatis的配置文件
            Reader reader = Resources.getResourceAsReader(resource);
            // 2,创建sqlSessionFactory对象,目的是获取sqlSession
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            // 3,创建可以执行SQL语句的sqlSession--工厂创建产品
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 4,执行SQL语句(Team.xml中的命名空间+sql语句id)
            List<Team> list = sqlSession.selectList("com.xxy.pojo.Team.queryAll");
            for (Team t : list) {
                System.out.println(t);
            }
            // 5,关闭sqlSession,释放资源
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2,案例2——根据ID查询单个对象

1,在Team.xml中添加sql语句

    <!--根据ID查询 parameterType="参数的类型",目前只支持一个参数 
    where teamId=#{id}: #{id}表示参数 id-自定义,只需要符合命名规范即可,没有实际对应意义 -->
    <select id="selectById" parameterType="java.lang.Integer" resultType="com.xxy.pojo.Team">
        select * from team where teamId=#{id};
    </select>

2,编写测试方法

测试类中有@Before和@After两种注解,使用这两种注解的方法可以用于处理一些准备工作和收尾工作

package com.xxy.test;

import com.xxy.pojo.Team;
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.Reader;
import java.util.List;

public class TeamTest {
    // mybatis的配置文件--相当于创建工厂的图纸
    private String resource = "mybatis.xml";
    private SqlSession sqlSession;

    @Test
    public void test02() {
        Team team = sqlSession.selectOne("com.xxy.pojo.Team.selectById", 1001);
        System.out.println(team);
    }

    @Before
    public void myBefore() {
        System.out.println("myBefore执行---");
        try {
            Reader reader = Resources.getResourceAsReader(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            sqlSession = sqlSessionFactory.openSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @After
    public void myAfter() {
        System.out.println("myAfter执行---");
        sqlSession.close();
    }
}

3,案例3——添加、修改、删除

1,在Team.xml中添加sql语句

    <!--mybatis3.3.1版本之前,sql语句中加分号会报错!-->

    <!--这条语句也可,加反引号是为了避免关键字冲突
    insert into team(teamName, location, createTime) value (#{teamId},#{location},#{createTime});-->
    <insert id="add" parameterType="com.xxy.pojo.Team">
        INSERT INTO `team` (`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
    </insert>

    <delete id="delete" >
        delete from team where teamId=#{id}
    </delete>

    <update id="update" parameterType="com.xxy.pojo.Team">
        update team set teamName=#{teamName}, location=#{location} where teamId=#{teamId};
    </update>

2,添加测试方法

    @Test
    public void testAdd() {
        Team team = new Team();
        team.setTeamName("申鹤");
        team.setLocation("璃月");
        team.setCreateTime(new Date());
        // 只执行下面这段语句的话,没有报错,但数据库中不会显示插入成功
        int num = sqlSession.insert("com.xxy.pojo.Team.add", team);// 配置文件中默认设置为不提交事务
        // 提交之后才能生效
        sqlSession.commit();// 增删改必须手动提交事务
        System.out.println(num);
    }

    @Test
    public void testDelete() {
        int num = sqlSession.delete("com.xxy.pojo.Team.delete", 1005);
        sqlSession.commit();
        System.out.println(num);
    }

    @Test
    public void testUpdate() {
        Team team = sqlSession.selectOne("com.xxy.pojo.Team.selectById", 1005);
        team.setTeamName("胡桃");
        team.setLocation("往生堂");
        int num = sqlSession.update("com.xxy.pojo.Team.update", team);
        sqlSession.commit();
        System.out.println(num);
    }

openSession中提供了布尔类型的参数,设置为true时,可以自动提交提交事务,但一般不这么做。

四,日志的使用

1,在pom.xml中引入log4j依赖

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

2,在resources目录下添加log4j.properties配置文件

# Global logging configuration info warning error
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

3,在mybatis配置文件中添加日志的配置

注意mybatis配置文件中各项配置的顺序

<configuration>
    <!--配置日志,注意顺序:查看属性点击configuration进入查看即可-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    ...

4,执行任意测试方法

五,Mybatis对象分析&架构

1,对象分析

1.1 Resources

Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

1.2 SqlSessionFactoryBuilder

SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 事实上使用SqlSessionFactoryBuilder的原因是将SqlSessionFactory这个复杂对象的创建交由Builder来执行,也就是使用了建造者设计模式。

建造者模式: 又称生成器模式,是一种对象的创建模式。 可以将一个产品的内部表象与产品的生成过程分割开来, 从而可以使一个建造过程生成具有 不同的内部表象的产品(将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示). 这样用户只需指定需要建造的类型就可 以得到具体产品,而不需要了解具体的建造过程和细节. 

在建造者模式中,角色分指导者(Director)与建造者(Builder): 用户联系指导者, 指导者指挥建造者, 最后得到产品. 建造者模式可以强制实行 一种分步骤进行的建造过程.

1.3 sqlSessionFactory

SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。

默认的 openSession()方法没有参数,它会创建有如下特性的 SqlSession:

1、会开启一个事务(也就是不自动提交)。

2、将从由当前环境配置的 DataSource 实例中获取 Connection 对象。事务隔离级别将会使用驱动或数据源的默认设置。

3、预处理语句不会被复用,也不会批量处理更新。

  • openSession(true):创建一个有自动提交功能的 SqlSession
  • openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
  • openSession():同 openSession(false

1.4 SqlSession

SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以SqlSession 对象的关闭结束。

SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其close()方法,将其关闭。再次需要会话,再次创建。SqlSession 在方法内部创建,使用完毕后关闭。

SqlSession 类中有超过 20 个方法,我们常用的几乎都是执行语法相关的方法。这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性和参数对象,参数可以是原生类型(自动装箱或包装类)、JavaBean、POJO 或 Map。

<T> T selectOne(String statement, Object parameter) 
<E> List<E> selectList(String statement, Object parameter) 
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey) 
int insert(String statement, Object parameter) 
int update(String statement, Object parameter) 
int delete(String statement, Object parameter) 

// selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象或 null 值。如果返回值多于一个,那么就会抛出异常。 

// selectMap 稍微特殊一点,因为它会将返回的对象的其中一个属性作为 key 值,将对象作为 value 值,从而将多结果集转为 Map 类型值。因为 
// 并不是所有语句都需要参数,所以这些方法都重载成不需要参数的形式。

2,架构

1、Mybatis.xml文件是mybatis框架的全局配置文件,配置了mybatis框架运行的环境等信息。

Mapper1.xml.....是SQL的映射文件,文件中配置了所有的操作数据库的sql语句,这些文件需要在全局配置文件中加载。

2、通过mybatis环境等配置信息构建SqlSessionFactroy ,相当于是产生连接池

3、由会话工厂创建SqlSession即会话(连接),操作数据库需要通过SqlSession进行的。

4、Mybatis底层自定义了Executor执行器的接口操作数据库,Executor接口有两个实现,一个基本的执行器,一个是缓存的执行器。

5、Mapped statement 也是mybatis框架一个底层的封装对象,他包装了mybatis配置信息以及sql映射信息。

Mapper.xml文件中的一个SQL语句对应一个Mapped statement对象,sql的id就是Mapped statement的id。

6、Mapped statement对SQL执行输入参数的定义,输入参数包括HashMap、基本类型、pojo,Executor通过Mapped statement在执行SQL语句前将输入java对象映射到sql语句中,执行完毕SQL之后,输出映射就是JDBC编码中的对preparedStatement 执行结果的定义。

六,使用原有的DAO方式开发

1,创建工具类MybatisUtil

public class MybatisUtil {
    private static SqlSessionFactory factory;

    static {
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader("mybatis.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //创建工厂
        factory=new SqlSessionFactoryBuilder().build(reader);//根据图纸创建出了工厂
    }

    /**
     * 获取连接
     * @return
     */
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = factory.openSession();
        return sqlSession;
    }

    /**
     * 关闭连接
     */
    public static void closeSqlSession(){
        if(sqlSession!=null){
            sqlSession.close();
        }
    }
}

2,创建TeamDao接口和实现类

public interface TeamDao {
    List<Team> queryAll();
    Team queryById(Integer teamId);
    int add(Team team);
    int update(Team team);
    int del(Integer teamId);
}
public class TeamDaoImpl implements TeamDao{
    /**
     * 查询所有球队
     * @return
     */
    @Override
    public List<Team> queryAll() {
        SqlSession sqlSession= MybatisUtil.getSqlSession();
        return  sqlSession.selectList("com.xxy.pojo.Team.queryAll");
    }
    /**
     * 根据id查询单个球队
     * @param teamId
     * @return
     */
    @Override
    public Team queryById(Integer teamId) {
        SqlSession sqlSession= MybatisUtil.getSqlSession();
        return sqlSession.selectOne("com.xxy.pojo.Team.queryById",teamId);
    }

    /**
     * 添加球队
     * @param team
     * @return
     */
    @Override
    public int add(Team team) {
        SqlSession sqlSession= MybatisUtil.getSqlSession();
        int num= sqlSession.insert("com.xxy.pojo.Team.add",team);
        sqlSession.commit();
        return num;
    }

    /**
     * 更新球队
     * @param team
     * @return
     */
    @Override
    public int update(Team team) {
        SqlSession sqlSession= MybatisUtil.getSqlSession();
        int num=  sqlSession.update("com.xxy.pojo.Team.update",team);
        sqlSession.commit();
        return num;
    }

    /**
     * 根据id删除球队
     * @param teamId
     * @return
     */
    @Override
    public int del(Integer teamId) {
        SqlSession sqlSession= MybatisUtil.getSqlSession();
        int num=  sqlSession.delete("com.xxy.pojo.Team.del",teamId);
        sqlSession.commit();
        return num;
    }
}

七,使用ThreadLocal优化工具类

1,ThreadLocal介绍

SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其close()方法,将其关闭。再次需要会话,再次创建。SqlSession 在方法内部创建,使用完毕后关闭。

ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。

线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本, 是Java中一种较为特殊的线程绑定机制,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。(类似于引用对象的拷贝 )

编写测试方法,测试ThreadLocal功能

public class ThreadLocalTest {
    //可以理解为一个容器:特殊点:只能盛放一个数据
    private ThreadLocal<String> threadLocal=new ThreadLocal<>();
    private List<String> list=new ArrayList<String>();

    class MyThread1 extends Thread{
        @Override
        public void run() {
            threadLocal.set("贾宝玉1");
            list.add("AAAA");
            System.out.println("MyThread1---threadLocal-----"+threadLocal.get());
            System.out.println("MyThread1---list-----"+list.get(0));
        }
    }
    class MyThread2 extends Thread{
        @Override
        public void run() {
            threadLocal.set("林黛玉2");
            list.add("BBBB");
            System.out.println("MyThread2---threadLocal-----"+threadLocal.get());
            System.out.println("MyThread2---list-----"+list.get(0));
        }
    }

    public static void main(String[] args) {
        ThreadLocalTest test=new ThreadLocalTest();
        MyThread1 t1=test.new MyThread1();
        MyThread2 t2=test.new MyThread2();
        t1.start();
        t2.start();
    }

    public static void main1(String[] args) {
        ThreadLocalTest test=new ThreadLocalTest();
        //添加数据
        test.threadLocal.set("wangLina");
        test.threadLocal.set("贾宝玉");//再次添加会覆盖前面的值
        //取出数据
        String s = test.threadLocal.get();
        System.out.println(s);
    }
}

main1 中的测试可以看出threadLocal中的值可以覆盖之前的值

main 中的测试可以看出,List中的变量是两个线程共享的,ThreadLocal中的值在各自线程中各不影响。

2,使用ThreadLocal优化工具类

由于dao中每次执行sql语句时,都需要先获取sqlSession,当一个线程A使用工具类获取sqlSession后即将关闭时,线程B可能也获取了sqlSession,由于sqlSession不是线程安全的,所以线程A的关闭操作可能会影响线程B。

因此采用ThreadLocal,将线程与资源进行绑定,这样线程间使用sqlSession的过程就不会相互影响。

public class MybatisUtil {
    private static ThreadLocal<SqlSession> sqlSessionThreadLocal=new ThreadLocal<>();
    private static SqlSessionFactory factory;

    static {
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader("mybatis.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //创建工厂
        factory=new SqlSessionFactoryBuilder().build(reader);//根据图纸创建出了工厂
    }

    /**
     * 获取连接
     * @return
     */
    public static SqlSession getSqlSession(){
        //从ThreadLocal中获取
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if(sqlSession==null) {
            //创建sqlSession
            sqlSession = factory.openSession();
            //将sqlSession与线程进行绑定
            sqlSessionThreadLocal.set(sqlSession);
        }
       return sqlSession;
    }

    /**
     * 关闭连接
     */
    public static void closeSqlSession(){
        //从ThreadLocal中获取
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if(sqlSession!=null){
            sqlSession.close();
            sqlSessionThreadLocal.remove();
        }
    }
}

八,使用Mapper动态代理

1,什么是Mapper接口

在前面例子中自定义 Dao 接口实现类时发现一个问题:Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。

所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对DB 进行操作。这种对 Dao 的实现方式称为 Mapper接口 的动态代理方式。

Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。

2,实现步骤

1,编写接口TeamMapper.java

package com.xxy.mapper;
import com.xxy.pojo.Team;
import java.util.List;

public interface TeamMapper {
    List<Team> queryAll();
    Team queryById(Integer teamId);
    int add(Team team);
    int update(Team team);
    int del(Integer teamId);
}

2,在TeamMapper接口的同一目录下创建TeamMapper.xml文件

与Team.xml内容几乎一样,只有namespace="com.xxy.mapper.TeamMapper"修改为接口的完全限定名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace="名称必须与映射的接口的名字一致,是完全限定名"-->
<mapper namespace="com.xxy.mapper.TeamMapper">

    <!--这条语句也可,加反引号是为了避免关键字冲突
    insert into team(teamName, location, createTime) value (#{teamId},#{location},#{createTime});-->
    <insert id="add" parameterType="com.xxy.pojo.Team">
        INSERT INTO `team` (`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
    </insert>

    <delete id="delete" >
        delete from team where teamId=#{id}
    </delete>

    <update id="update" parameterType="com.xxy.pojo.Team">
        update team set teamName=#{teamName}, location=#{location} where teamId=#{teamId};
    </update>

    <!-- id="自定义名称,id不能重复;相当于dao中的方法名称"
    resultType="使用的要求:实体类中的属性名与表中的列名一致" -->
    <select id="queryAll" resultType="com.xxy.pojo.Team">
        select * from team;
    </select>

    <!--根据ID查询 parameterType="参数的类型",目前只支持一个参数
    where teamId=#{id}: #{id}表示参数 id-自定义,只需要符合命名规范即可,没有实际对应意义 -->
    <select id="selectById" parameterType="java.lang.Integer" resultType="com.xxy.pojo.Team">
        select * from team where teamId=#{id};
    </select>
</mapper>

3,在mybatis.xml配置文件中注册映射文件

    <mappers>
        ...
        <mapper resource="com/xxy/mapper/TeamMapper.xml"></mapper>
    </mappers>
</configuration>

4,getMapper方法获取代理对象,并执行sql语句(原dao功能)

只需调用 SqlSession 的 getMapper()方法,即可获取指定接口的实现类对象。

public class TeamTest {
    private SqlSession sqlSession = MyBatisUtil.getSqlSession();

    @Test
    public void testTeamMapper() {
        TeamMapper teamMapper = sqlSession.getMapper(TeamMapper.class);
        List<Team> teams = teamMapper.queryAll();
        for (Team t : teams) {
            System.out.println(t);
        }
    }
}

增加、删除、修改的语句仍需要使用sqlSession.commit()才可以生效

5,配置文件也可以放在resources,但是需要保持原来的包结构(同包,同名)

可以将原先pojo、mapper中的xml文件放在resources目录下,但要保证同包、同名!

需要注意,resources目录下创建目录时不会因为 "点" 而自动分层,比如创建目录时输入com.xxy.mapper时,创建的是名为com.xxy.mapper的目录,而不是com/xxy/mapper,因此需要手动一级一级的创建。

此外,建议直接通过拖拽将原TeamMapper.xml文件移动到新的目录下,这样IDE可以自动的修改依赖关系,避免出错

3,实现原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值