MyBatis接口代理方式实现Dao层

目录

MyBatis接口代理方式实现Dao层

区别
1、selectlist和getMapper区别
//4.执行映射配置文件中的sql语句,并接收结果
list = sqlSession.selectList("StudentMapper.selectAll");
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); 
//5.通过实现类对象调用方法,接收结果
result = mapper.delete(id);

两者其实并无区别,只不过StudentMapper利用代理对象操作sqlSession.selectList();

2、namespace的作用
<mapper namespace="StudentMapper">
<mapper namespace="com.zhu.mapper.StudentMapper">

1.完全限定名(比如“com.mypackage.MyMapper.selectAllThings”)将被直接查找并且找到即用。

2.短名称(比如“selectAllThings”)如果全局唯一也可以作为一个单独的引用。如果不唯一,有两个或两个以上的相同名称(比如“com.foo.selectAllThings ”和“com.bar.selectAllThings”),那么使用时就会收到错误报告说短名称是不唯一的,这种情况下就必须使用完全限定名。

Mybatis中namespace用于绑定dao接口,dao接口的方法对应mapper中的sql语名

接口代理方式 - 实现规则

1)映射配置文件中的名称空间必须和Dao层接口的全类名相同

2)映射配置文件中的增删改查的标签id属性必须和Dao层接口层的方法名相同

3)映射配置文件中的增删改查标签的parameterType属性必须和Dao层接口方法的参数相同

4)映射配置文件中的增删改查标签的resultType属性必须和Dao层接口方法的返回值相同

接口代理方式 - 代码实现

1)删除mapper层接口的实现类

2)修改映射配置文件

3)修改service层接口的实现类,采用接口代理方式实现功能

在写StudentMapper.java时

注意

1、resultType="student" 方法的返回值为List<Student>

2、parameterType="student"方法的参数为Student student

3、如何取出参数中的值 #{}

· MyBatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>
  <!--默认名为类名首字母小写-->
    <typeAliases>
        <package name="com.zhu.bean"/>
    </typeAliases>
    <environments default="mysql">
        <!--environment配置数据库环境 id 属性唯一标识 -->
        <environment id="mysql">
            <!-- transactionManager事务管理。type属性,采用JDBC默认的事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--dataSource数据源信息 type属性 连接池 MyBatis默认有三种数据源-->
            <dataSource type="POOLED">
                <!--property获取数据库连接的配置信息-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- mappers引入映射配置文件 -->
    <mappers>
        <!-- mapper 引入指定的映射配置文件  resource属性指定映射配置文件的名称 -->
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>
· StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    mapper: 核心根标签
    namespace属性: 名称空间
-->
<mapper namespace="com.zhu.mapper.StudentMapper">
    <select id="selectAll" resultType="student">
        SELECT * FROM student
    </select>

    <insert id="insert" parameterType="student">
        INSERT INTO student VALUES (#{id},#{name},#{age});
    </insert>

    <delete id="delete" parameterType="int">
        DELETE FROM student WHERE id=#{id};
    </delete>
</mapper>
· controller - studentController.java
public class StudentController {
    private StudentService studentService = new StudentServiceImpl();

    @Test
    public void selectAll(){
        List<Student> list = studentService.selectAll();
        for (Student student : list) {
            System.out.println(student);
        }
    }
   @Test
    public void insert(){
        Student st = new Student(10,"路飞",23);
        Integer insert = studentService.insert(st);
        System.out.println(insert);
    }
    @Test
    public void delete(){
        Integer s = 3;
        Integer i = studentService.delete(3);
        System.out.println(i);
    }
}
· mapper - StudentMapper.java
public interface StudentMapper{
    /*
        注意: resultType 值为 student  指定结果映射的对象类型
                方法的返回值为   List<Student>
        resultType:
                1、基本类型  :resultType=基本类型
                2、List类型 :resultType=List中元素的类型
                3、Map类型  单条记录:resultType =map
                          多条记录:resultType =Map中value的类型
              方法的返回值可以为List<Student>
        parameterType:
                 基本数据类型:int,string,long,Date;
                 复杂数据类型:类和Map
                如何获取参数中的值:
                               基本数据类型:#{value}或${value} 获取参数中的值
                                复杂数据类型:#{属性名}或${属性名}  ,map中则是#{key}或${key}
     */
    public abstract List<Student> selectAll();

    Integer insert(Student student);

    Integer delete(Integer integer);
}
· service - StduentService.java - StudentServiceImpl.java
public interface StudentService {
    List<Student> selectAll();
    Integer insert(Student student);
    Integer delete(Integer integer);
}
public class StudentServiceImpl implements StudentService {
    @Override
    public List<Student> selectAll() {
        List<Student> list = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = factory.openSession(true);

            //获取StudentMapper接口的实现类对象
            //获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。
            //操作数据库主要是通过SQL语句,那么只要找到SQL语句然后执行不就可以
            //sqlSession.getMapper()的内部产生了StudentMapper的实现类,那怎么产生的呢?
            //动态代理
            /*
                被代理对象:真实的对象
                代理对象:内存中的一个对象
             */
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            //StudentMapper studentMapper1 = new StudentServiceImpl();
            //return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
            /*
                类加载器:和被代理对象使用相同的类加载器
                接口类型Class数组:和被代理对象使用相同接口
                代理规则:完成代理增强的功能
             */
            //通过代理对象调用方法,接收结果
            list = mapper.selectAll();
            
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
接口代理方式 - 问题
分析动态代理对象如何生成的?

通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过getMapper() 方法最终获取到 org.apache.ibatis.binding.MapperProxy代理对象,然后执行功能,而这个代理对象正是 MyBatis使用了 JDK 的动态代理技术,帮助我们生成了代理实现类对象。从而可以进行相关持久化操作。

分析方法是如何执行的?

动态代理实现类对象在执行方法的时候最终调用了mapperMethod.execute() 方法,这个方法中通过 switch 语句根据操作类型来判断是新增、修改、删除、查询操作,最后一步回到了MyBatis 最原生的 SqlSession方式来执行增删改查。

小结
  • 原生:Dao ---> DaoImpl

  • 接口:Mapper ---> xxMapper.xml

  • 接口代理方式可以让给我们只编写接口即可,而实现类对象由MyBatis生成

  • 获取动态代理对象

    SqlSession功能类中的getMapper(Mapper接口.class)方法。

MyBatis映射配置文件 - 动态SQL

"if标签" +
<select id="selectCondition" resultType="student" parameterType="student">
        SELECT * FROM student
        <where>
                <if test="id != null">
                    id = #{id}
                </if>
                <if test="name != null">
                    AND name = #{name}
                </if>
                <if test="age != null">
                    AND age = #{age}
                </if>
        </where>
    </select>
"foreach"

foreach 用来迭代用户传过来的List或者Array

如:使用foreach元素来构建in子语句

collection : 指定要遍历的集合名称 list、array

item : 用来临时存放迭代集合中当前元素的值,便于在foreach中使用

open :将该属性指定的值添加到foreach迭代后拼出字符串的开始

close :将该属性指定的值添加到foreach迭代拼出字符串的结尾

separator : 用来分割foreach元素迭代的每个元素

<!--SELECT * FROM student WHERE id IN (1,2);
-->
    <select id="selectByIds" resultType="student" parameterType="list">
        SELECT * from student
        <where>
            <foreach collection="list" open="id IN(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

    <select id="selectByIds2" resultType="student" parameterType="list">
        SELECT * from student where id IN
            <foreach collection="list" open="(" close=")" item="id" separator=",">
                #{id}
            </foreach>
    </select>

<!--如果是 selectById(List<Student> stus) 这种-->
    <select id="selectByIds3" resultType="student" parameterType="list">
        SELECT * from student
            <where>
                <foreach collection="list" open="id IN(" close=")" item="stu" separator=",">
                    #{stu.id}
                </foreach>
            </where>
    </select>

使用

StudentMapper.java

List<Student> selectByIds(List<Integer> list);

StudentService.java

List<Student> selectByIds(List<Integer> list);
sql片段抽取
<sql id="select">SELECT * FROM student</sql>

使用

<include refid="select"/>
小结
  • 动态 SQL 指的就是 SQL 语句可以根据条件或者参数的不同进行动态的变化。
  • :条件标签。
  • :条件判断的标签。
  • :循环遍历的标签。
  • :抽取SQL 片段的标签。
  • :引入SQL 片段的标签。

MyBatis 核心配置文件 - 分页查询

使用PageHelper

功能实现

(1) 导入jar包 分页插件pagehelper-5.1.10.jar 和依赖的包 jsqlparser-3.1.jar

(2) 在核心配置文件中集成分页助手插件

(3) 在测试类中使用分页助手相关API实现分页功能。

MyBatisConfig.xml

<!-- 环境配置顺序
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,
objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
使用

StudentServiceImpl.java

PageHelper.startPage(1,3); 一定要写在SQL执行之前

//通过分页助手来实现分页功能
// 第一页:显示3条数据
//PageHelper.startPage(1,3);
// 第二页:显示3条数据
//PageHelper.startPage(2,3);
// 第三页:显示3条数据
PageHelper.startPage(3,3);

//5.调用实现类的方法,接收结果
List<Student> list = mapper.selectAll();PageHelper.startPage(0,1);

相关API

返回值方法名说明
longgetTotal()获取总条数
intgetPages()获取总页数
intgetPageNum()获取当前页
intgetPageSize()获取每页显示条数
intgetPrePage()获取上一页
intgetNextPage()获取下一页
booleanisIsFirstPage()获取是否是第一页
booleanisIsLastPage()获取是否是最后一页

MyBatis 多表操作

外键字段的建立是体现表与表关系的所在

  • 一对一 : 在任意一方建立外键,关联对方的主键

用户基本信息表 和 用户详细信息表

  • 一对多 : 在多的一方建立外键,关联一的一方主键

店铺表 和 商品表

用户表 和 订单表

  • 多对多 : 借助中间表,中间表至少两个字段,分别关联两张表的主键

学生表 和 课程表

用户表 和 兴趣爱好表

多表操作 一对一

模型 : 人和身份证 、 一个人只有一张身份证

数据准备

CREATE DATABASE db2;

CREATE TABLE person(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20),
	age INT
);
INSERT INTO person VALUES (NULL,‘张三‘,23);
INSERT INTO person VALUES (NULL,‘李四‘,24);
INSERT INTO person VALUES (NULL,‘王五‘,25);

CREATE TABLE card(
	id INT PRIMARY KEY AUTO_INCREMENT,
	number VARCHAR(30),
	pid INT,
	CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
)
INSERT INTO card VALUES (NULL,‘12345‘,1);
INSERT INTO card VALUES (NULL,‘23456‘,2);
INSERT INTO card VALUES (NULL,‘34567‘,3);

bean

Student.java

public class Person {
    private Integer id;     //主键id
    private String name;    //人的姓名
    private Integer age;    //人的年龄
}

Card.java

public class Card {
    private Integer id;
    private String number;
    private Person p;
}

OneToOneMapper.xml

<mapper namespace="com.mybatis.oneToOne.OneToOneMapper">
<!--
<resultMap>是Mybatis的结果集封装
-->
<!--通过 <resultMap>: 配置字段和对象属性的映射关系标签
    属性
       id 属性:resultMap 的唯一标识,此 id 值用于 select 元素 resultMap 属性的引用。
    type 属性:表示该 resultMap 的映射结果类型(通常是 Java 实体类)。
 子节点
       id 子节点:一般对应数据库中该行的主键 id,设置此项可以提升 MyBatis 性能。
       配置主键映射关系标签
    result 子节点:映射到 JavaBean 的某个 “简单类型” 属性,如基础数据类型、包装类等。
       配置非主键映射关系标签
  子节点属性
    column 属性:表示从数据库中查询的字段名或别名。表中字段名称
    property 属性:表示查询出来的字段对应的值赋给实体对象的哪个属性。实体对象变量名称
说明:子节点 id 和 result 均可实现最基本的结果集映射,将列映射到简单数据类型的属性。
这两者唯一不同的是:在比较对象实例时 id 将作为结果集的标识属性。
这有助于提高总体性能,特别是应用缓存和嵌套结果映射的时候。
而若要实现高级结果映射,就需要学习下面两个配置项: association 和 collection。
    association:映射到 JavaBean 的某个 “复杂类型” 属性,比如 JavaBean 类,
    即 JavaBean 内部嵌套一个复杂数据类型(JavaBean)属性,这种情况就属于复杂类型的关联。
    但是需要注意: association 仅处理一对一的关联关系。
     association:配置被包含对象的映射关系
        property:被包含对象的变量名
        javaType:被包含对象的数据类型
-->

        <resultMap id="oneToOne" type="card">
            <id column="cid" property="id"/>
            <result column="number" property="number"/>

            <association property="p" javaType="person">
                <id column="pid" property="id"/>
                <result column="name" property="name"/>
                <result column="age" property="age"/>
            </association>
        </resultMap>

    <select id="selectAll" resultMap="oneToOne">
            SELECT
                c.id cid,
                number,
                pid,
                NAME,
                age
            FROM
                card c , person p
            WHERE
                c.pid = p.id
    </select>
</mapper>

MyBatisConfig.xml

<mappers>
        <!-- mapper 引入指定的映射配置文件  resource属性指定映射配置文件的名称 -->
       <mapper resource="com/mybatis/oneToOne/OneToOneMapper.xml"></mapper>
    </mappers>

测试

//4.获取OneToOneMapper接口的实现类对象
        OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);

        //5.调用实现类的方法,接收结果
        List<Card> list = mapper.selectAll();

        //6.处理结果
        for (Card c : list) {
            System.out.println(c);
        }
多表操作 一对多

数据准备

多表操作  一对多  班级对学生
CREATE TABLE classes(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARBINARY(20)

);
INSERT INTO classes VALUES (NULL,‘s一班‘);
INSERT INTO classes VALUES (NULL,‘s二班‘);

CREATE TABLE student(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(30),
	age INT,
	cid INT,
	CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
);
INSERT INTO student VALUES (NULL,‘张三‘,23,1);
INSERT INTO student VALUES (NULL,‘李四‘,24,1);
INSERT INTO student VALUES (NULL,‘王五‘,25,2);
INSERT INTO student VALUES (NULL,‘赵六‘,26,2);

bean

public class Student {
    private Integer id;
    private String name;
    private Integer age;
}
public class Classes {
    private Integer id;
    private String name;
    private List<Student> students; //班级中所有学生对象
}

OneToManyMapper.xml

<mapper namespace="com.mybatis.oneToMany.OneToManyMapper">

    <resultMap id="oneToMany" type="classes">
        <id column="cid" property="id"/>
        <result column="cname" property="name"/>
        <!--
           collection:配置被包含的集合对象映射关系
           property:被包含对象的变量名
           ofType:被包含对象的实际数据类型
        -->
       <collection property="students" ofType="student">
           <id column="sid" property="id"/>
           <result column="sname" property="name"/>
           <result column="sage" property="age"/>
       </collection>
    </resultMap>
    <select id="selectAll" resultMap="oneToMany">
            SELECT
                c.id cid,
                c.name cname,
                s.id sid,
                s.name sname,
                s.age sage
            FROM
                classes c,student s
            WHERE
                c.id=s.cid
    </select>

测试

//4.获取OneToManyMapper接口的实现类对象
        OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);

        //5.调用实现类的方法,接收结果
        List<Classes> classes = mapper.selectAll();

        //6.处理结果
for (Classes cls : classes) {
    System.out.println(cls.getId() + "," + cls.getName());
    List<Student> students = cls.getStudents();
    for (Student student : students) {
        System.out.println("\t" + student);
    }
 }
多表操作 多对多

数据准备

学生和课程 一个学生可以选择多门课程 一个课程可以被多个学生选择

CREATE TABLE course(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20)
);

INSERT INTO course VALUES (NULL,‘语文‘);
INSERT INTO course VALUES (NULL,‘数学‘);

CREATE TABLE stu_cr(
	id INT PRIMARY KEY AUTO_INCREMENT,
	sid INT,
	cid INT,
	CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
	CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);

INSERT INTO stu_cr VALUES (NULL,1,1);
INSERT INTO stu_cr VALUES (NULL,1,2);
INSERT INTO stu_cr VALUES (NULL,2,1);
INSERT INTO stu_cr VALUES (NULL,2,2);

bean

Course.java

public class Course {
    private Integer id;
    private String name;
}

Student.java

public class Student {
    private Integer id;
    private String name;
    private Integer age;

    private List<Course> courses;  //学生所选的课程集合
}

ManyToManyMapper.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.mybatis.manyToMany.ManyToManyMapper">
    <resultMap id="ManyToMany" type="student">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="sage" property="age"/>
<!--    <collection>:配置被包含集合对象的映射关系标签。
               属性:
                   property 属性:被包含集合对象的变量名
                   ofType 属性:集合中保存的对象数据类型-->
        <collection property="courses" ofType="course">
            <id column="cid" property="id"/>
            <result column="cname" property="name"/>
        </collection>
    </resultMap>
    <select id="selectAll" resultMap="ManyToMany">
        SELECT
            sc.sid,
            s.name sname,
            s.age sage,
            sc.cid,
            c.name cname
        FROM
            student s,course c,stu_cr sc
        WHERE
            sc.sid=s.id AND sc.cid=c.id
    </select>
</mapper>

处理结果

//5.调用实现类的方法,接收结果
List<Student> students = mapper.selectAll();

        //6.处理结果
for (Student student : students) {
    System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
    List<Course> courses = student.getCourses();
    for (Course cours : courses) {
        System.out.println("\t" + cours);
    }
        }
小结
<!--一对一 所有标签如下

            <resultMap>:配置字段和对象属性的映射关系标签。 
               属性:
                   id 属性:唯一标识 
                   type 属性:实体对象类型 

            <id>:配置主键映射关系标签。 
            <result>:配置非主键映射关系标签。 
               属性:
                   column 属性:表中字段名称 
                   property 属性: 实体对象变量名称
         (*)<association>:配置被包含对象的映射关系标签。                            属性:
                   property 属性:被包含对象的变量名 
                   javaType 属性:被包含对象的数据类型
-->


<!--多对多 & 一对多 所有标签如下

            <resultMap>:配置字段和对象属性的映射关系标签。 
               属性:
                   id 属性:唯一标识 
                   type 属性:实体对象类型 

            <id>:配置主键映射关系标签。 
            <result>:配置非主键映射关系标签。 
               属性:
                   column 属性:表中字段名称 
                   property 属性: 实体对象变量名称
         (*)<collection>:配置被包含集合对象的映射关系标签。 
               属性:
                   property 属性:被包含集合对象的变量名 
                   ofType 属性:集合中保存的对象数据类型
-->

扩展

针对结果集而创建对应的javabean类,来接收结果集数据,这种javabean在领域驱动模型中称之为 : VO

相关推荐