原创

Mybatis的mapper动态代理

温馨提示:
本文最后更新于 2018年02月01日,已超过 2,489 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

1. 未使用mapper动态代理

工程截图

SQL

CREATE TABLE `t_student` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NULL,
  `age` INT NULL,
  `score` DOUBLE NULL,
  PRIMARY KEY (`id`));

pow

Maven导包。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ssm</artifactId>
        <groupId>com.lzhpo.study</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-mybatis-improvement</artifactId>
    <packaging>war</packaging>

    <name>2-mybatis-improvement Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</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>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>2-mybatis-improvement</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
        <!--如果不添加resources里面的内容的话,maven是不会将xml文件发布到编译后的classes目录下,这样就会导致mybatis到不到该文件。-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>

        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

Student

包含Getter/Setter、构造器、toString方法。

package com.lzhpo.bean;

import org.apache.ibatis.type.Alias;

/**
 * <p>
 * Create By IntelliJ IDEA
 * Author:lzhpo
 * </p>
 */
//@Alias("student")
public class Student {
    private int id;
    private String name;
    private int age;
    private double score;

    /**
     * 创建构造函数
     * @param name
     * @param age
     * @param score
     */
    //带id
    public Student(int id, String name, int age, double score) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.score = score;
    }
    //不带id
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    /**
     * Setter/Getter
     * @return
     */
    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    /**
     * toString
     * @return
     */
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

StudentMapper.xml

里面是我们写的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:命名空间。
id:方法,不能重复。
parameterType:传入参数类型。
-->
<mapper namespace="lzhpo">

    <!--添加学生-->
    <!--parameterType可省略-->
    <insert id="insertStudent">
        INSERT INTO t_student(name,age,score) VALUES (#{name},#{age},#{score})
          <!--获取自增主键-->
          <selectKey resultType="int" keyProperty="id" order="AFTER">
              select @@identity
          </selectKey>
    </insert>

    <!--删除学生-->
    <delete id="deleteById">
        delete from t_student where id=#{id}
    </delete>

    <!--更新操作-->
    <update id="updateStudent">
        update t_student set name=#{name},age=#{age},score=#{score} where id=#{id}
    </update>

    <!--查询所有-->
    <!--注意:resultType要写上单条数据对应的类,查询中不要使用*,效率低下。-->
    <select id="selectAllStudent" resultType="student">
        select id,name,age,score from t_student
    </select>

    <!--查询单条-->
    <select id="selectById" resultType="student">
        select id,name,age,score from t_student where id=#{id}
    </select>

    <!--模糊查询(name)-->
    <!--注意:另一种写法,不推荐,(SELECT id,name,age,score FROM t_student where name like '%${value}%')因为是字符串拼接,所以可能会引起sql注入的问题。-->
    <select id="selectByName" resultType="student">
        select id,name,age,score from t_student where name like '%' #{name} '%'
    </select>
</mapper>

mybatis.xml

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">
<!--environment可配置多个(多数据源),id就要修改为不一样。
<environments default="development">:使用哪个environment。-->
<configuration>

    <!--db.properties-->
    <properties resource="db.properties"/>

    <!--mybatis的别名,有顺序的,不能乱放位置。注册实体类的全限定名的别名。-->
    <typeAliases>
        <!--方式1:不方便,如果有多个,需要一个个写,效率低。 -->
        <!--<typeAlias type="com.lzhpo.bean.Student" alias="student"/>-->

        <!--方式2:mybatis会在这个包(com.lzhpo.bean)下搜索需要的bean;mybatis会自动的取别名,一般是首字母小写;
            如果需要自己配置别名,就需要在com.lzhpo.bean.Student类中添加注解@Alias("student")-->
        <package name="com.lzhpo.bean"/>
    </typeAliases>

    <!--environments:这里选择开发环境(development)-->
    <environments default="development">

        <!--1.开发环境-->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <!--2.测试环境-->
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.test.driver}"/>
                <property name="url" value="${jdbc.test.url}"/>
                <property name="username" value="${jdbc.test.username}"/>
                <property name="password" value="${jdbc.test.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--注册映射文件-->
        <mapper resource="com/lzhpo/dao/StudentMapper.xml"/>
    </mappers>

</configuration>

db.properties

数据库的配置信息。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=123456

log4j.properties

打印日志消息的。

log4j.rootLogger=trace,console

#控制台附加器
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern= [%-5p][%d{yyyy-MM-dd HH:mm:ss}]%m%n

StudentDao

增删改查方法的接口。

package com.lzhpo.dao;

import com.lzhpo.bean.Student;

import java.util.List;

/**
 * <p>
 * Create By IntelliJ IDEA
 * Author:lzhpo
 * </p>
 */
public interface StudentDao {
    //添加学生
    void insertStudent(Student student);
    //根据id删除学生
    void deleteById(int id);
    //删除学生
    void updateStudent(Student student);
    //查询所有学生数据
    List<Student> selectAllStudent();
    //根据id查询学生
    Student selectStudentById(int id);
    //模糊查询(name)
    List<Student> selectByName(String name);
}

StudentDaoImpl

StudentDao的实现类。

package com.lzhpo.dao.impl;

import com.lzhpo.bean.Student;
import com.lzhpo.dao.StudentDao;
import com.lzhpo.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;

/**
 * StudentDao的实现类
 *
 * <p>
 * Create By IntelliJ IDEA
 * Author:lzhpo
 * </p>
 */
public class StudentDaoImpl implements StudentDao {

    //创建成员变量sqlSession
    private SqlSession sqlSession;

    /**
     * 添加学生
     * @param student
     */
    @Override
    public void insertStudent(Student student) {
        //SqlSession继承了AutoCloseable接口,所以可以自动关闭
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //新增数据库操作
        sqlSession.insert("insertStudent",student);
        //手动提交sqlSession事
        // 要自动提交事务需要在MybatisUtil中 return sqlSessionFactory.openSession(true); 改为true。
        sqlSession.commit();
    }

    /**
     * 根据id删除学生
     * @param id
     */
    @Override
    public void deleteById(int id) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        sqlSession.delete("deleteById",id);
        sqlSession.commit();
    }

    /**
     * 更新学生
     * @param student
     */
    @Override
    public void updateStudent(Student student) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        sqlSession.update("updateStudent",student);
        sqlSession.commit();
    }

    /**
     * 查询所有学生
     * @return
     */
    @Override
    public List<Student> selectAllStudent() {
        List<Student> result = null;
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        result = sqlSession.selectList("selectAllStudent");
        return result;
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    @Override
    public Student selectStudentById(int id) {
        Student student = null;
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        student = sqlSession.selectOne("selectById",id);
        return student;
    }

    /**
     * 模糊查询(name)
     * @param name
     * @return
     */
    @Override
    public List<Student> selectByName(String name) {
        List<Student> result = null;
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        result = sqlSession.selectList("selectByName",name);
        return result;
    }
}

MybatisUtil

将过去SqlSession对象的操作封装成一个方法。SqlSession对象是由SqlSessionFactory对象创建的,SqlSessionFactory是线程安全的,所以可以使用单例模式来创建SqlSessionFactory对象。

package com.lzhpo.util;

/**
 * <p>
 * Create By IntelliJ IDEA
 * Author:lzhpo
 * </p>
 */

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 java.io.IOException;
import java.io.InputStream;

/**
 * DCL的单例模式
 */
public class MybatisUtil {

    //不需要将构造方法私有化,因为这里的单例只是保证外界使用当前工具时创建一个SqlSessionFactory对象就行
//    private MyBatisUtil() {
//
//    }

    private static volatile SqlSessionFactory sqlSessionFactory;

    public static SqlSession getSqlSession() {
        try {
            if (sqlSessionFactory == null) {
                //读取主配置文件
                InputStream input = Resources.getResourceAsStream("mybatis.xml");
                //线程安全锁机制,双重判断,保证new一个SqlSession
                synchronized (MybatisUtil.class) {
                    if (sqlSessionFactory == null){
                        sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
                    }
                }

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

        return sqlSessionFactory.openSession();
    }


}

Junit测试

package com.lzhpo.test;

import com.lzhpo.bean.Student;
import com.lzhpo.dao.StudentDao;
import com.lzhpo.dao.impl.StudentDaoImpl;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

/**
 * Junit测试
 *
 * <p>
 * Create By IntelliJ IDEA
 * Author:lzhpo
 * </p>
 */
public class StudentTest01 {

    private StudentDao studentDao;

    /**
     * 所有操作都需要studentDao = new StudentDaoImpl();
     * 直接写在Junit测试的Before
     */
    @Before
    public void initStudentDao(){
        studentDao = new StudentDaoImpl();
    }

    /**
     * 添加学生
     */
    @Test
    public void insertStudent(){
        //Student student = new Student("鞠婧祎",25,100);
        Student student1 = new Student("周大生",50,98);
        //此时id为0
        System.out.println(student1);
        studentDao.insertStudent(student1);
        //可以获取自增主键
        System.out.println(student1);
    }

    /**
     * 根据id删除学生
     */
    @Test
    public void deleteById(){
        studentDao.deleteById(2);
    }

    /**
     * 更新学生
     */
    @Test
    public void updateStudent(){
        //创建更新后的学生
        Student student2 = new Student("周杰伦",32,99);
        //需要更新哪个学生
        student2.setId(4);
        //调用studeDao里面的方法
        studentDao.updateStudent(student2);
    }

    /**
     * 查询所有学生
     */
    @Test
    public void selectAllStudent(){
        List<Student> studentList = studentDao.selectAllStudent();
        //java8新增的Lambda表达式
        studentList.forEach((s ->{
            System.out.println(s);
        }));
    }

    /**
     * 根据id查询
     */
    @Test
    public void selectById(){
        Student student = studentDao.selectStudentById(1);
        System.out.println(student);
    }

    /**
     * 模糊查询(name)
     */
    @Test
    public void selectByName(){
        List<Student> studentList = studentDao.selectByName("周");
        studentList.forEach((s -> {
            System.out.println(s);
        }));
    }
}

2. 使用mapper动态代理

mapper的动态代理介绍

在之前的例子中,我们在dao接口的实现类中写了一些获取sqlSession并调用其方法的代码,这些代码实际上没有什么实质的作用,具体SQL方面的操作我们都写在mapper文件中了,因此可以mybatis抛开这些实现类,以后无需编写这些实现类了,直接通过dao接口来定位到mapper中的SQL语句,这种方式被称为mapper的动态代理。

如何使用?

将上面的StudentDaoImpl(StudentDao的实现类)删除

这些代码实际上没有什么实质的作用,具体SQL方面的操作我们都写在mapper文件中了,因此可以mybatis抛开这些实现类,以后无需编写这些实现类了,直接通过dao接口来定位到mapper中的SQL语句。

修改StudentMapper.xml的命名空间

<mapper namespace="lzhpo">修改为<mapper namespace="com.lzhpo.dao.StudentDao">

如下:

<mapper namespace="com.lzhpo.dao.StudentDao">

这样子mybatis会自动和com.lzhpo.dao.StudentDao一一对应。

修改Junit测试类

添加SqlSession和StudentDao的成员变量,将之前的@Before@After修改。

    private SqlSession sqlSession;

    private StudentDao studentDao;

    /**
     * 测试时先执行该方法创建StudentDao对象
     */
    @Before
    public void initStudentDao(){
        sqlSession = MybatisUtil.getSqlSession();
        //通过该方法可以获取StudentDao的对象
        studentDao = sqlSession.getMapper(StudentDao.class);
    }

    /**
     * 执行完成后需要关闭sqlSession
     */
    @After
    public void closeSession() {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }

Mybatis的mapper动态代理总结

  1. 在开发的过程中只需要写Dao层的借口,无需写其实现类,实现类有框架自己补充。
  2. 不需要写Dao层的实现类,减轻程序员的压力,简化代码。
本文目录