Mybatis学习教程(详细版)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jokeMqc/article/details/81483792

1、对原生态jdbc程序(单独使用jdbc开发)问题总结

先来看一部分代码

Public static void main(String[] args) {

            Connection connection = null;

            PreparedStatement preparedStatement = null;

            ResultSet resultSet = null;

 

            try {

                //加载数据库驱动

                Class.forName("com.mysql.jdbc.Driver");

 

                //通过驱动管理类获取数据库链接

                connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql");

                //定义sql语句 ?表示占位符

            String sql = "select * from user where username = ?";

                //获取预处理statement

                preparedStatement = connection.prepareStatement(sql);

                //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值

                preparedStatement.setString(1, "王五");

                //向数据库发出sql执行查询,查询出结果集

                resultSet =  preparedStatement.executeQuery();

                //遍历查询结果集

                while(resultSet.next()){

                    System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));

                }

            } catch (Exception e) {

                e.printStackTrace();

            }finally{

                //释放资源

                if(resultSet!=null){

                    try {

                        resultSet.close();

                    } catch (SQLException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                if(preparedStatement!=null){

                    try {

                        preparedStatement.close();

                    } catch (SQLException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                if(connection!=null){

                    try {

                        connection.close();

                    } catch (SQLException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

 

            }

 

        }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

上面代码有如下几个问题:

  • 数据库连接,使用时创建,不使用就关闭,对数据库进行频繁连接开启和关闭,造成数据库资源的浪费 
    • 解决:使用数据库连接池管理数据库连接
  • 将sql 语句硬编码到Java代码中,如果sql语句修改,需要对java代码重新编译,不利于系统维护 
    • 解决:将sql语句设置在xml配置文件中,即使sql变化,也无需重新编译
  • 向preparedStatement中设置参数,对占位符位置和设置参数值,硬编码到Java文件中,不利于系统维护 
    • 解决:将sql语句及占位符,参数全部配置在xml文件中
  • 从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。 
    • 解决:将查询的结果集,自动映射成java对象

2、Mybatis框架原理(掌握)

  • 什么是Mybatis? 
    • mybatis是一个持久层的框架,是apache下的顶级项目。
    • mybatis托管到goolecode下,后来托管到github下:mybatis Github地址
    • mybatis让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。
    • mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
  • Mybatis原理图: 

3、Mybatis入门程序

  • Mybatis和SpringMVC实现订单商品案例
  • 需求 
    • 根据用户的id查询用户信息
    • 根据用户的名称模糊查询用户信息
    • 添加用户
    • 删除用户
    • 更新用户
  •  

Mybatis的运行环境(jar包)

    •  

从这里点击下载

    •  

笔者下载的是: 

下载mybatis-3.4.6.zip解压即可 

      • lib下:依赖包
      • mybatis-3.4.6.jar:核心 包
      • mybatis-3.4.6.pdf,操作指南

目录结构: 
 
jar包结构 
 
过程中需要数据库的连接,junit的调试,注意要将jar包build path!

log4j.properties

# Global logging configuration# 开发环境下,日志级别要设置成DEBUG或者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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

SqlMapConfig.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>

    <!-- 和spring整合后 environments配置将废除-->

    <environments default="development">

        <environment id="development">

            <!-- 使用jdbc事务管理-->

            <transactionManager type="JDBC" />

            <!-- 数据库连接池-->

            <dataSource type="POOLED">

                <property name="driver" value="com.mysql.jdbc.Driver" />

                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />

                <property name="username" value="root" />

                <property name="password" value="" />

            </dataSource>

        </environment>

    </environments>

    <!--加载映射文件-->

    <mappers>

        <mapper resource="sqlmap/User.xml"/>

    </mappers></configuration>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

User.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"><!--

    命名空间,作用为,对sql进行分类化管理,理解sql隔离,

    注意:使用mapper代理方法开发,namespace有特殊作用

--><mapper namespace="test">

    <!--在映射文件中配置sql-->

    <!--

        findUserById

        通过select执行数据库查询

        id:标识映射文件的sql

        将sql语句封装到mappedStatement对象中,所以将id称为Statement的id

        #{}:表示一个占位符

        parameterType:指定输入参数的类型

        #{id}:其中的id表示接收输入的参数,参数的名称就是id,如果输入参数类型为简单类型,那么#{}中的参数可以任意,可以是value或其他

        resultType:指定sql输出结果的所映射的Java对象类型,select指定的resultType表示将单条记录映射成的Java对象

    -->

    <select id="findUserById" parameterType="int" resultType="com.nuc.mybatis.po.User">

        select * from user where id=#{VALUE }

    </select>

 

 

 

 

    <!--

        findUserByName

        ${}:表示拼接字符串,将接收到的sql不加任何修饰拼接在sql语句里

        使用${}拼接sql,可能会引起sql注入,一般不建议使用

        ${value}:接收参数的内容,如果传入的的是简单类型,${}中只能使用value

    -->

    <select id="findUserByName" parameterType="java.lang.String" resultType="com.nuc.mybatis.po.User">

        select * from user WHERE username LIKE '%${value}%'

    </select>

 

 

 

 

    <!--

        添加用户

        parameterType:指定参数类型为pojo类型

        #{}中指定pojo的属性名,接收到的pojo对象的属性值,mybatis通过OGNL获取对象的值

        SELECT LAST_INSERT_ID():得到刚刚insert进去的记录的主键值,只适用于主键自增

        非主键自的则需要使用uuid()来实现,表的id类型也得设置为tring(详见下面的注释)

        keyProperty:将查询到的主键值设置到SparameterType指定的对象的哪个属性

        order:SELECT LAST_INSERT_ID()执行顺序,相当于insert语句来说它的实现顺序

 

    -->

    <insert id="insertUser" parameterType="com.nuc.mybatis.po.User">

        <!--uuid()-->

        <!--

            <selectKey keyProperty="id" order="AFTER" resultType="java.lang.String">

              SELECT uuid()

            </selectKey>

            insert into user (id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})

        -->

      <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">

          SELECT LAST_INSERT_ID()

      </selectKey>

      insert into user (username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})

    </insert>

 

 

    <delete id="deleteUser" parameterType="java.lang.Integer">

        delete from user where id=#{id}

    </delete>

 

    <update id="updateUser" parameterType="com.nuc.mybatis.po.User">

        UPDATE user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}

    </update></mapper>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

User.java

package com.nuc.mybatis.po;

import java.util.Date;

public class User {

    //用户po

    //属性名和数据库字段名对应

    private int id;

    private String username;// 用户姓名

    private String sex;// 性别

    private Date birthday;// 生日

    private String address;// 地址

 

    public int getId() {

        return id;

    }

 

    public void setId(int id) {

        this.id = id;

    }

 

    public String getUsername() {

        return username;

    }

 

    public void setUsername(String username) {

        this.username = username;

    }

 

    public String getSex() {

        return sex;

    }

 

    public void setSex(String sex) {

        this.sex = sex;

    }

 

    public Date getBirthday() {

        return birthday;

    }

 

    public void setBirthday(Date birthday) {

        this.birthday = birthday;

    }

 

    public String getAddress() {

        return address;

    }

 

    public void setAddress(String address) {

        this.address = address;

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

MybatisFirst.java

package com.nuc.mybatis.first;

import com.nuc.mybatis.po.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.Date;import java.util.List;

public class MybatisFirst {

    //根据id查询用户信息,得到一条记录

    @Test

    public void findUserByIdTest() throws IOException {

        //mybatis配置文件

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过工厂得到SqlSession

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //通过SqlSession操作数据库

        //第一个参数:映射文件中的statement的id,等于namespace+"."+statement的id

        //第二个参数:指定和映射文件中所匹配的所有parameterType的类型

        //sqlSession.selectOne()的结果是映射文件中所匹配的resultType类型的对象

        User user = sqlSession.selectOne("test.findUserById",1);

        System.out.println(user);

        //释放资源

        sqlSession.close();

    }

    //根据用户名称查询用户列表

    @Test

    public void findUserByName() throws IOException {

        //mybatis配置文件

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过工厂得到SqlSession

        SqlSession sqlSession = sqlSessionFactory.openSession();

        List<User> list= sqlSession.selectList("test.findUserByName","小明");

        System.out.println(list);

        sqlSession.close();

    }/*

    小结:

    selectOne和selectList:

 

    selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。

    selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。

    如果使用selectOne报错:

    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

 

 */

    @Test

    public void insertUserTest() throws IOException {

        //mybatis配置文件

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过工厂得到SqlSession

        SqlSession sqlSession = sqlSessionFactory.openSession();

        User user = new User();

        user.setUsername("宋江涛");

        user.setBirthday(new Date());

        user.setSex("男");

        user.setAddress("山西");

        //list中的user和映射文件User.xml中的resultType的类型一直

        sqlSession.insert("test.insertUser",user);

        //提交事务

        sqlSession.commit();

        //获取主键

        System.out.println(user.getId());

        sqlSession.close();

    }

    //删除用户

    @Test

    public void deleteUserTest() throws IOException {

        //mybatis配置文件

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过工厂得到SqlSession

        SqlSession sqlSession = sqlSessionFactory.openSession();

 

        //list中的user和映射文件User.xml中的resultType的类型一直

        sqlSession.delete("test.deleteUser",30);

        //提交事务

        sqlSession.commit();

        sqlSession.close();

    }

 

    //更新用户

    @Test

    public void updateUserTest() throws IOException {

        //mybatis配置文件

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过工厂得到SqlSession

        SqlSession sqlSession = sqlSessionFactory.openSession();

        User user = new User();

        user.setId(27);

        user.setUsername("宋江涛new2");

        user.setBirthday(new Date());

        user.setSex("男");

        user.setAddress("山西太原new");

        //list中的user和映射文件User.xml中的resultType的类型一直

        sqlSession.update("test.updateUser",user);

        //提交事务

        sqlSession.commit();

        sqlSession.close();

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122

数据库的设计图 

 
部分测试结果 
 
jdbc的测试程序,前面已经沾过。 
重点内容在代码的注释中均已说明

mybatis和hibernate本质区别和应用场景

  •  

hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。 
对sql语句进行优化、修改比较困难的。

    • 应用场景: 
      • 适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。。
  •  

mybatis:专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全 的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。

    • 应用场景: 
      • 适用与需求变化较多的项目,比如:互联网项目。

企业进行技术选型,以低成本 高回报作为技术选型的原则,根据项目组的技术力量进行选择。

4、Mybatis开发dao的方法

  •  

原始dao开发方法(程序需要编写dao接口和dao实现类)(掌握)

    • 在原有项目的基础上,src目录下创建com.nuc.mybatis.dao
    • 在这个包里有两个文件,一个是UserDao.java,另一个是UserDaoImpl.java这个类继承接口UserDao.java
    • 在创建一个和src同级的文件夹(记的把它标记为sources)具体方法见详情
    •  

在test下创建com.nuc.mybatis.test,创建类UserDaoImplTest.java

    •  

具体目录结构: 
 
结构中的相关mapper请先忽略

    •  

UserDao.java

package com.nuc.mybatis.dao;

import com.nuc.mybatis.po.User;

public interface UserDao {

    //dao原始开发

    //根据id查询用户信息

    public User findUserById(int id) throws Exception;

    //添加用户

    public void insertUser(User user) throws Exception;

    //删除用户

    public void deleteUser(int id) throws Exception;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

UserDaoImpl.java

package com.nuc.mybatis.dao;

import com.nuc.mybatis.po.User;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSession;

public class UserDaoImpl implements UserDao {

    //原生态的dao

    //需要向dao实现类里注入SqlSessionFactory

    //通过构造方法

    private SqlSessionFactory sqlSessionFactory;

 

    public UserDaoImpl(SqlSessionFactory sqlSessionFactory){

        this.sqlSessionFactory=sqlSessionFactory;

    }

    @Override

    public User findUserById(int id) throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        User user = sqlSession.selectOne("test.findUserById",id);

        sqlSession.close();

        return user;

    }

 

    @Override

    public void insertUser(User user) throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        sqlSession.insert("test.insertUser",user);

        sqlSession.commit();

        sqlSession.close();

 

    }

 

    @Override

    public void deleteUser(int id) throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        sqlSession.insert("test.deleteUser",id);

        sqlSession.commit();

        sqlSession.close();

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

UserDaoImplTest.java

package com.nuc.mybatis.test;

 

import com.nuc.mybatis.mapper.UserMapper;

import com.nuc.mybatis.po.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.Before;

import org.junit.Test;

 

import java.io.InputStream;

import java.util.List;

 

public class UserMapperTest {

    //原始dao的测试

    private SqlSessionFactory sqlSessionFactory;

    @Before

    public void setUp() throws Exception{

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        sqlSessionFactory = new

                SqlSessionFactoryBuilder().build(inputStream);

    }

    @Test

    public void testFindUserById() throws Exception{

      SqlSession sqlSession = sqlSessionFactory.openSession();

       //创建UserMapper的对象,mybatis自动调用

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findUserById(1);

        System.out.println(user);

 

    }

    @Test

    public void testFindUserByName() throws Exception{

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //创建UserMapper的对象,mybatis自动调用

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        List<User> list= userMapper.findUserByName("宋江涛");

        System.out.println(list);

    }

    //其他的测试,可以照猫画虎

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

测试结果: 

  •  

Mybatis的mapper接口(相当于dao接口)代理开发方法(掌握)

  •  

根据上面的结构图创建相应文件,源码如下:

  •  

UserMapper.java

package com.nuc.mybatis.mapper;

import com.nuc.mybatis.po.User;

import java.util.List;

public interface UserMapper {

    //mapper代理开发和dao开发对比//    mapper接口,相当于dao接口,mybatis可以自动生成mapper接口实现类的代理对象

    //根据id查询用户信息

    public User findUserById(int id) throws Exception;

    //根据用户名查询用户列表

    public List<User>findUserByName(String name) throws Exception;

    //添加用户

    public void insertUser(User user) throws Exception;

    //删除用户

    public void deleteUser(int id) throws Exception;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

UserMapper.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.nuc.mybatis.mapper.UserMapper">

    <select id="findUserById" parameterType="int" resultType="com.nuc.mybatis.po.User">

        select * from user where id=#{VALUE }

    </select>

    <select id="findUserByName" parameterType="java.lang.String" resultType="com.nuc.mybatis.po.User">

        select * from user WHERE username LIKE '%${value}%'

    </select>

 

    <insert id="insertUser" parameterType="com.nuc.mybatis.po.User">

 

      <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">

          SELECT LAST_INSERT_ID()

      </selectKey>

      insert into user (username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})

    </insert>

 

 

    <delete id="deleteUser" parameterType="java.lang.Integer">

        delete from user where id=#{id}

    </delete>

 

    <update id="updateUser" parameterType="com.nuc.mybatis.po.User">

        UPDATE user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}

    </update></mapper>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在测试之前需要在SqlMapConfig.xml中加载mapper.xml这个映射文件 
 
UserMapperTest.java

package com.nuc.mybatis.test;

 

import com.nuc.mybatis.mapper.UserMapper;

import com.nuc.mybatis.po.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.Before;

import org.junit.Test;

 

import java.io.InputStream;

import java.util.List;

 

public class UserMapperTest {

    //原始mapper的测试

    private SqlSessionFactory sqlSessionFactory;

    @Before

    public void setUp() throws Exception{

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        sqlSessionFactory = new

                SqlSessionFactoryBuilder().build(inputStream);

    }

    @Test

    public void testFindUserById() throws Exception{

      SqlSession sqlSession = sqlSessionFactory.openSession();

       //创建UserMapper的对象,mybatis自动调用

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findUserById(1);

        System.out.println(user);

 

    }

    @Test

    public void testFindUserByName() throws Exception{

    //返回列表测试

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //创建UserMapper的对象,mybatis自动调用

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        List<User> list= userMapper.findUserByName("宋江涛");

        System.out.println(list);

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

部分测试结果: 
 
总结:

  • 原始dao开发问题 
    • dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
    • 调用sqlsession方法时将statement的id硬编码了
  •  

mapper开发

    • 只需要编写两个文件,mapper.java,mapper.xml。即可,不需要类来继承它。
    •  

mapper开发只需要遵守几个规范即可

      • 在mapper.xml中namespace等于mapper接口地址 
      • mapper.java接口中的方法名和mapper.xml中statement的id一致 
         
      • mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。 
         
      • mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。 

    •  

其实,以上开发规范主要是对下边的代码进行统一生成:

    •  

User user = sqlSession.selectOne("test.findUserById", id);

sqlSession.insert("test.insertUser", user);

。。。。

      • 1
      • 2
      • 3
    •  

mapper接口方法参数只能有一个是否影响系统 开发?mapper接口方法参数只能有一个,系统是否不利于扩展维护?

      • 系统 框架中,dao层的代码是被业务层公用的。
      • 即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。

注意:持久层方法的参数可以包装类型、map。。。,service方法中建议不要使用包装类型(不利于业务层的可扩展)。

5、Mybatis配置文件SqlMapConfig.xml

  •  

properties(属性)

    • 将数据库连接的参数单独配置在,db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。
    • 好处:方便对参数进行统一管理,其它xml可以引用该db.properties
    •  

特性: MyBatis 将按照下面的顺序来加载属性:

      • 在 properties 元素体内定义的属性首先被读取。
      • 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
      • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

建议:

      • 不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
      • 在properties文件中定义属性名要有一定的特殊性,如XXXXX.XXXXX.XXXX 
         
  • settings(全局配置参数) 
    • mybatis框架在运行时可以调整一些运行参数。比如:开启二级缓存、开启延迟加载。。全局参数将会影响mybatis的运行行为。具体如下: 
       
       
  • typeAliases(类型别名)(重点
    • 单个定义 
    • 批量定义(常用) 
       
      这样在其他地方就可以使用,例如: 
    • mybatis默认支持的别名 
  • typeHandlers(类型处理器) 
    • mybatis中通过typeHandlers完成jdbc类型和java类型的转换。通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义.
    • mybatis支持的类型处理器 
       
  • objectFactory(对象工厂) 
    • 这个自行查看下载mybatis时附带的pdf文件,用的不多
  • plugins(插件)
  • environments(环境集合属性对象) 
    • environment(环境子属性对象) 
      • transactionManager(事务管理)
      • dataSource(数据源)
  • mappers(映射器) 
    • 通过resource 
    • 通过class 
    • 通过package(推荐使用) 

6、Mybatis核心

  •  

Mybatis输入映射(掌握)

    • 通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的包装类型 
      • 传递pojo的包装对象 
        • 需求:完成用户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息、其它信息,比如商品、订单的)
        • 针对上边需求,建议使用自定义的包装类型的pojo。 
          在包装类型的pojo中将复杂的查询条件包装进去。

目录结构 
 
UserCustom.java

package com.nuc.mybatis.po;

public class UserCustom extends User {

    //可扩展用户信息

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

UserQueryVo.java

package com.nuc.mybatis.po;

public class UserQueryVo {

    //这里包装所需的查询条件

 

    //用户查询条件

    private UserCustom userCustom;

 

    public UserCustom getUserCustom() {

        return userCustom;

    }

 

    public void setUserCustom(UserCustom userCustom) {

        this.userCustom = userCustom;

    }

 

    //可包装其他的查询条件,订单,商品。。。

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

UserMapper.xml中配置新的查询 
 
UserMapperTest.java中新增测试 
 
测试结果 

  •  

Mybatis输出映射(掌握)

    • 一、resultType 
      • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
      • 如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
      • 只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象

在上面的输入中,属于每一列都成功映射 
 
当我们修改查询语句为: 

其中id,起了别名叫做id_ 
测试结果: 
 
很明显,由于查询条件,生日和地址为空,由于id的映射失败,导致查询id失败,只有sex一列映射成功,故查询成功(1代表男,2代表女)

    • 1.resultType的输出简单类型 
      • 需求:用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。

如法炮制,对mapper.xml编辑和mapper.java编辑 
 
 
测试代码(复制修改) 
 
测试结果 

总结:查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。

  •  

2.resultType的输出pojo对象和pojo列表

    • 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。 
      • 在mapper.java指定的方法返回值类型不一样: 
        • 1、输出单个pojo对象,方法返回值是单个对象类型
        • 2、输出pojo对象list,方法返回值是List<简单java对象> 
  •  

二、resultMap

    • mybatis中使用resultMap完成高级输出结果映射。
    • 实例: 
      • 1、在usermapper.xml定义resultMap 
      • 2、在usermapper.xml中定义查询 
      • 3、在usermapper.java中定义方法 
      • 4、编写测试方法 
      • 5、测试结果 
      • 6、总结 
        • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功
        • 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

7、Mybatis的动态sql(掌握)

  • 什么是动态sql? 
    • mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
    • 在原来的usermapper.xml中修改用户综合查询总数为下面代码

     <!--用户综合查询总数-->

    <select id="findUserCount" parameterType="com.nuc.mybatis.po.UserQueryVo"

            resultType="int">

        select count(*) from user

        <!--where可以自动去掉第一个and-->

        <where>

            <if test="userCustom!=null">

                <if test="userCustom.sex!=null and userCustom.sex!=''">

                    and user.sex=#{userCustom.sex}

                </if>

                <if test="userCustom.username!=null and userCustom.username!=''">

                    and user.username=#{userCustom.username}

                </if>

            </if>

        </where>

    </select>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 修改测试类及测试结果 
  • sql片段 
    • 将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。方便程序员进行开发
    • 先在mapper.xml中定义一个sql片段

<!--sql片段

    经验:基于单表来定义sql片段,这样,sql片段的可重用度就高

    在sql片段中不要用where,因为你很有可能查询条件不止一个

    -->

    <sql id="Query_user">

        <if test="userCustom!=null">

            <if test="userCustom.sex!=null and userCustom.sex!=''">

                and user.sex=#{userCustom.sex}

            </if>

            <if test="userCustom.username!=null and userCustom.username!=''">

                and user.username=#{userCustom.username}

            </if>

        </if>

    </sql>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 将之前的sql语句修改如下:

<!--用户综合查询总数-->

    <select id="findUserCount" parameterType="com.nuc.mybatis.po.UserQueryVo"

            resultType="int">

        select count(*) from user

        <!--where可以自动去掉第一个and-->

        <where>

            <!--引用sql的id,如果refid引用的sql的id不在本mapper中,则需要家其他mapper的namespace-->

            <include refid="Query_user"/>

        </where>

    </select>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 测试和之前的一样
  • foreach 
    • 向sql传递数组或List,mybatis使用foreach解析
    • 在用户查询列表和查询总数的statement中增加多个id输入查询。即实现SELECT * FROM USER WHERE id=1 OR id=10 OR id=16
    • 先修改sql

<sql id="Query_user">

        <if test="userCustom!=null">

            <if test="userCustom.sex!=null and userCustom.sex!=''">

                and user.sex=#{userCustom.sex}

            </if>

            <if test="userCustom.username!=null and userCustom.username!=''">

                and user.username=#{userCustom.username}

            </if>

        </if>

        <if test="ids!=null">

            <!--

                collection:指定输入对象集合中的属性

                item:每次遍历生成的对象名

                open:开始遍历拼接的串

                close:结束时拼接的串

                separator:每个对象的中间拼接的串

                我们要实现 and(id=1 OR id=10 OR id=16)

            -->

            <foreach collection="ids" item="user_id" open="and(" close=")" separator="OR">

              <!--每次遍历拼接的串-->

                id=#{user_id}

            </foreach>

        </if>

    </sql>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 添加属性,及其get set 方法 
  • 修改测试类

//    用户综合查询总数

@Test

    public void testFindUser() throws Exception{

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //创建UserMapper的对象,mybatisCount自动调用

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //创建包对象,设置查询条件

        UserQueryVo userQueryVo = new UserQueryVo();

        UserCustom userCustom = new UserCustom();//        由于这里使用动态sql所以不设置某个值,就不会拼接在sql语句中//        userCustom.setSex("1");

        userCustom.setUsername("小明");

        List<Integer> list = new ArrayList<>();

        list.add(1);

        list.add(10);

        list.add(16);

        userQueryVo.setIds(list);

        userQueryVo.setUserCustom(userCustom);

        int count= userMapper.findUserCount(userQueryVo);

        System.out.println(count);

    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 测试结果 
     
    sql语句成功修改!

8、订单商品数据模型分析

  •  

数据模型分析思路

    • 1、每张表记录的数据内容 
      • 分模块对每张表记录的内容进行熟悉,相当 于你学习系统 需求(功能)的过程
    • 2、每张表重要的字段设置 
      • 非空字段、外键字段
    • 3、数据库级别表与表之间的关系 
      • 外键关系
    • 4、表与表之间的业务关系 
      • 在分析表与表之间的业务关系时一定要建立 在某个业务意义基础上去分析。

下来拿之前的数据库的表为例,具体字段不作说明 
 
根据上面的步骤。 
第一步:每张表记录的数据内容

    • 用户表user:记录了购买商品的用户信息
    • 订单表:orders:记录了用户所创建的订单(购买商品的订单)
    • 订单明细表:orderdetail:记录了订单的详细信息即购买商品的信息
    • 商品表:items:记录了商品信息

第二步:每张表重要的字段设置 
第三步:数据库级别表与表之间的关系

    • 表与表之间的业务关系:在分析表与表之间的业务关系时需要建立 在某个业务意义基础上去分析。

先分析数据库级别之间有关系的表之间的业务关系:

    •  

usre和orders:

      • user—>orders:一个用户可以创建多个订单,一对多
      • orders—>user:一个订单只由一个用户创建,一对一
    •  

orders和orderdetail:

      • orders—>orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多
      • orderdetail—> orders:一个订单明细只能包括在一个订单中,一对一
    • orderdetail和itesm: 
      • orderdetail—>itesms:一个订单明细只对应一个商品信息,一对一
      • items–> orderdetail:一个商品可以包括在多个订单明细 ,一对多

第四步:表与表之间的业务关系

    • 再分析数据库级别没有关系的表之间是否有业务关系: 
      • orders和items: 
        • orders和items之间可以通过orderdetail表建立 关系。

9、高级结果集映射

  •  

一对一 
需求:查询订单信息,关联查询创建订单的用户信息

  •  

分析:

    • 确定查询的主表:订单表
    • 确定查询的关联表:用户表
    • 关联查询使用内链接?还是外链接?
    •  

由于orders表中有一个外键(user_id),通过外键关联查询用户表只能查询出一条记录,可以使用内链接。

    •  

SELECT 
orders.*, 
USER.username, 
USER.sex, 
USER.address 
FROM 
orders, 
USER 
WHERE orders.user_id = user.id

    •  
    • 1、用resultType实现 
      • 在po包中创建:Orders类
      • 在po包中创建:OrdersCustom类继承 Orders类
      • 在mapper包中创建:OrdersMapperCustom接口
      • 在mapper包中创建:OrdersMapperCustom.xml
      • 在test包中创建:OrdersMapperTest类

Orders.java

package com.nuc.mybatis.po;

import java.util.Date;import java.util.List;

public class Orders {

    private Integer id;

 

    private Integer userId;

 

    private String number;

 

    private Date createtime;

 

    private String note;

 

    //用户信息

    private User user;

 

    //订单明细

    private List<Orderdetail> orderdetails;

 

    public Integer getId() {

        return id;

    }

 

    public void setId(Integer id) {

        this.id = id;

    }

 

    public Integer getUserId() {

        return userId;

    }

 

    public void setUserId(Integer userId) {

        this.userId = userId;

    }

 

    public String getNumber() {

        return number;

    }

 

    public void setNumber(String number) {

        this.number = number == null ? null : number.trim();

    }

 

    public Date getCreatetime() {

        return createtime;

    }

 

    public void setCreatetime(Date createtime) {

        this.createtime = createtime;

    }

 

    public String getNote() {

        return note;

    }

 

    public void setNote(String note) {

        this.note = note == null ? null : note.trim();

    }

 

    public User getUser() {

        return user;

    }

 

    public void setUser(User user) {

        this.user = user;

    }

 

    public List<Orderdetail> getOrderdetails() {

        return orderdetails;

    }

 

    public void setOrderdetails(List<Orderdetail> orderdetails) {

        this.orderdetails = orderdetails;

    }    

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

OrdersCustom .java

package com.nuc.mybatis.po;

public class OrdersCustom extends Orders {

    private String username;

    private String sex;

    private String address;

    public String getUsername() {

        return username;

    }

    public void setUsername(String username) {

        this.username = username;

    }

    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;

    }

 

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

OrdersMapperCustom.java

package com.nuc.mybatis.mapper;

import java.util.List;

public interface OrdersMapperCustom {

    public List<OrdersMapperCustom> findOrdersUser()throws Exception;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

OrdersMapperCustom .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.nuc.mybatis.mapper.OrdersMapperCustom">

    <!--resultType:返回值的映射的pojo对象-->

    <select id="findOrdersUser" resultType="com.nuc.mybatis.po.OrdersCustom">

      SELECT

      orders.*,

      user.username,

      user.sex,

      user.address

      FROM

      orders,

      user

      WHERE orders.user_id = user.id

    </select>

</mapper>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

OrdersMapperTest.java

package com.nuc.mybatis.test;

 

import com.nuc.mybatis.mapper.OrdersMapperCustom;

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.Before;

import org.junit.Test;

 

import java.io.InputStream;

import java.util.List;

 

public class OrdersMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before

    public void setUp() throws Exception{

        String resource = "SqlMapConfig.xml";

        InputStream inputStream = Resources.getResourceAsStream(resource);

        //创建会话工厂

        sqlSessionFactory = new

                SqlSessionFactoryBuilder().build(inputStream);

    }

    @Test

    public void testFindOrdersUser() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 创建代理对象

        OrdersMapperCustom ordersMapperCustom = sqlSession

                .getMapper(OrdersMapperCustom.class);

        // 调用maper的方法

        List<OrdersMapperCustom> list = ordersMapperCustom.findOrdersUser();

        System.out.println(list);

        sqlSession.close();

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

测试结果 

  • 2、用resultMap实现 
    • 实现思路:使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。 
      • 第一步:Orders类中添加User 的user属性。上面的代码已经添加
      • 第二步:mapper.xml中定义ResultMap及其查询
      • 第三步:接口中定义相应的方法
      • 最后测试

第三步:

<resultMap type="com.nuc.mybatis.po.Orders" id="OrdersUserResultMap">

        <!-- 配置映射的订单信息 -->

        <!-- id:指定查询列中的唯 一标识,订单信息的中的唯 一标识,如果有多个列组成唯一标识,配置多个id

            column:订单信息的唯 一标识 列

            property:订单信息的唯 一标识 列所映射到Orders中哪个属性

          -->

        <id column="id" property="id"/>

        <result column="user_id" property="userId"/>

        <result column="number" property="number"/>

        <result column="createtime" property="createtime"/>

        <result column="note" property="note"/>

        <!-- 配置映射的关联的用户信息 -->

        <!-- association:用于映射关联查询单个对象的信息

        property:要将关联查询的用户信息映射到Orders中哪个属性

         -->

        <association property="user"  javaType="com.nuc.mybatis.po.User">

            <!-- id:关联查询用户的唯 一标识

            column:指定唯 一标识用户信息的列

            javaType:映射到user的哪个属性

             -->

            <id column="user_id" property="id"/>

            <result column="username" property="username"/>

            <result column="sex" property="sex"/>

            <result column="address" property="address"/>

        </association>

    </resultMap>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

<!-- 查询订单关联查询用户信息,使用resultmap -->

    <select id="findOrdersUserResultMap" resultMap="OrdersUserResultMap">

        SELECT

        orders.*,

        USER.username,

        USER.sex,

        USER.address

        FROM

        orders,

        USER

        WHERE orders.user_id = user.id

    </select>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第四步

//查询订单关联查询用户使用resultMap

    public List<Orders> findOrdersUserResultMap()throws Exception;

  • 1
  • 2

测试

 

    @Test

    public void testFindOrdersResultMap() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 创建代理对象

        OrdersMapperCustom ordersMapperCustom = sqlSession

                .getMapper(OrdersMapperCustom.class);

        // 调用maper的方法

        List<Orders> list = ordersMapperCustom.findOrdersUserResultMap();

        System.out.println(list);

        sqlSession.close();

    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

总结

  •  

resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用resultType。

  •  
  •  

resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的属性中。resultMap可以实现延迟加载,resultType无法实现延迟加载。

  •  
  •  

一对多

    • 需求:查询订单及订单明细的信息。(根据数据库模型分析的结果来查询)
    • 使用resultMap: 
       
      要求:对orders映射不能出现重复记录。 
      解决思路: 
      • 在orders.java类中添加List<>, orderDetails属性。
      • 最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中。
      • 映射成的orders记录数为两条(orders信息不重复)
      • 每个orders中的orderDetails属性存储了该 订单所对应的订单明细

添加属性在之前已经实现,且注释。 
只需要配置xml文件和接口,写一个测试类即可

<!-- 订单及订单明细的resultMap

    使用extends继承,不用在中配置订单信息和用户信息的映射

     -->

    <resultMap type="com.nuc.mybatis.po.Orders" id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">

        <!-- 订单信息 -->

        <!-- 用户信息 -->

        <!-- 使用extends继承,不用在中配置订单信息和用户信息的映射 -->

 

        <!-- 订单明细信息

        一个订单关联查询出了多条明细,要使用collection进行映射

        collection:对关联查询到多条记录映射到集合对象中

        property:将关联查询到多条记录映射到com.nuc.mybatis.po.Orders哪个属性

        ofType:指定映射到list集合属性中pojo的类型

         -->

        <collection property="orderdetails" ofType="com.nuc.mybatis.po.Orderdetail">

            <!-- id:订单明细唯 一标识

            property:要将订单明细的唯 一标识 映射到com.nuc.mybatis.po.Orderdetail的哪个属性

              -->

            <id column="orderdetail_id" property="id"/>

            <result column="items_id" property="itemsId"/>

            <result column="items_num" property="itemsNum"/>

            <result column="orders_id" property="ordersId"/>

        </collection>

    </resultMap>

    <!-- 查询订单关联查询用户及订单明细,使用resultmap -->

    <select id="findOrdersAndOrderDetailResultMap" resultMap="OrdersAndOrderDetailResultMap">

        SELECT

        orders.*,

        USER.username,

        USER.sex,

        USER.address,

        orderdetail.id orderdetail_id,

        orderdetail.items_id,

        orderdetail.items_num,

        orderdetail.orders_id

        FROM

        orders,

        USER,

        orderdetail

        WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id

    </select>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

 //查询订单(关联用户)及订单明细

    public List<Orders> findOrdersAndOrderDetailResultMap() throws Exception;@Test

    public void testfindOrdersAndOrderDetailResultMap() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 创建代理对象

        OrdersMapperCustom ordersMapperCustom = sqlSession

                .getMapper(OrdersMapperCustom.class);

        // 调用maper的方法

        List<Orders> list = ordersMapperCustom.findOrdersAndOrderDetailResultMap();

        System.out.println(list);

        sqlSession.close();

    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

总结:

  • mybatis使用resultMap的collection对关联查询的多条记录映射到一个list集合属性中。
  • 使用resultType实现:将订单明细映射到orders中的orderdetails中,需要自己处理,使用双重循环遍历,去掉重复记录,将订单明细放在orderdetails中。
  •  

多对多

    • 需求:查询用户及用户购买商品信息。
    • sql语句 
      • 查询主表是:用户表
      • 关联表:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表:orders、orderdetail、items
      • SELECT 
        orders.*, 
        USER.username, 
        USER.sex, 
        USER.address, 
        orderdetail.id orderdetail_id, 
        orderdetail.items_id, 
        orderdetail.items_num, 
        orderdetail.orders_id, 
        items.name items_name, 
        items.detail items_detail, 
        items.price items_price 
        FROM 
        orders, 
        USER, 
        orderdetail, 
        items 
        WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
  •  

映射思路

    • 将用户信息映射到user中。
    • 在user类中添加订单列表属性List orderslist,将用户创建的订单映射到orderslist
    • 在Orders中添加订单明细列表属性List<>orderdetials,将订单的明细映射到orderdetials
    • 在OrderDetail中添加Items属性,将订单明细所对应的商品映射到Items
  •  

mapper.xml

  •  

    <!-- 查询用户及购买的商品信息,使用resultmap -->

    <select id="findUserAndItemsResultMap" resultMap="UserAndItemsResultMap">

        SELECT

        orders.*,

        USER.username,

        USER.sex,

        USER.address,

        orderdetail.id orderdetail_id,

        orderdetail.items_id,

        orderdetail.items_num,

        orderdetail.orders_id,

        items.name items_name,

        items.detail items_detail,

        items.price items_price

        FROM

        orders,

        USER,

        orderdetail,

        items

        WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id

    </select>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • resultMap定义

   <!-- 查询用户及购买的商品 -->

    <resultMap type="com.nuc.mybatis.po.User" id="UserAndItemsResultMap">

        <!-- 用户信息 -->

        <id column="user_id" property="id"/>

        <result column="username" property="username"/>

        <result column="sex" property="sex"/>

        <result column="address" property="address"/>

 

        <!-- 订单信息

        一个用户对应多个订单,使用collection映射

         -->

        <collection property="ordersList" ofType="com.nuc.mybatis.po.Orders">

            <id column="id" property="id"/>

            <result column="user_id" property="userId"/>

            <result column="number" property="number"/>

            <result column="createtime" property="createtime"/>

            <result column="note" property="note"/>

 

            <!-- 订单明细

        一个订单包括 多个明细

         -->

            <collection property="orderdetails" ofType="com.nuc.mybatis.po.Orderdetail">

                <id column="orderdetail_id" property="id"/>

                <result column="items_id" property="itemsId"/>

                <result column="items_num" property="itemsNum"/>

                <result column="orders_id" property="ordersId"/>

                <!-- 商品信息

         一个订单明细对应一个商品

          -->

                <association property="items" javaType="com.nuc.mybatis.po.Items">

                    <id column="items_id" property="id"/>

                    <result column="items_name" property="name"/>

                    <result column="items_detail" property="detail"/>

                    <result column="items_price" property="price"/>

                </association>

            </collection>

        </collection>

    </resultMap>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • mapper.java

 //查询用户购买商品信息

    public List<User>  findUserAndItemsResultMap()throws Exception;

  • 1
  • 2
  •  

测试 

  •  

多对多查询总结

    • 将查询用户购买的商品信息明细清单,(用户名、用户地址、购买商品名称、购买商品时间、购买商品数量)
    • 针对上边的需求就使用resultType将查询到的记录映射到一个扩展的pojo中,很简单实现明细清单的功能。
    • 一对多是多对多的特例,如下需求: 
      • 查询用户购买的商品信息,用户和商品的关系是多对多关系。 
        • 需求1:查询字段:用户账号、用户名称、用户性别、商品名称、商品价格(最常见)企业开发中常见明细列表,用户购买商品明细列表,使用resultType将上边查询列映射到pojo输出。
        • 需求2:查询字段:用户账号、用户名称、购买商品数量、商品明细(鼠标移上显示明细)使用resultMap将用户购买的商品明细列表映射到user对象中。
  •  

总结:使用resultMap是针对那些对查询结果映射有特殊要求的功能,,比如特殊要求映射成list中包括 多个list

  •  
  • resultMap总结 
    • resultType: 
      • 作用:将查询结果按照sql列名pojo属性名一致性映射到pojo中。
      • 场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)即可。
    • resultMap: 
      • 使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。
      • association: 
        • 作用:将关联查询信息映射到一个pojo对象中。
        • 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。
        • 使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。
      • collection: 
        • 作用:将关联查询信息映射到一个list集合中。
        • 场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询结果映射到list集合中。

10、Mybatis延迟加载

  •  

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

    • 需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
  •  

延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

  •  
  •  

不使用mybatis提供的association及collection中的延迟加载功能,如何实现延迟加载??

    • 实现方法如下: 
      • 定义两个mapper方法: 
        • 1、查询订单列表
        • 2、根据用户id查询用户信息
    • 实现思路:先去查询第一个mapper方法,获取订单信息列表。在程序中(service),按需去调用第二个mapper方法去查询用户信息。

总之:使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。

11、Mybatis查询缓存

  • 什么是查询缓存? 
    • mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
  • 为什么要用缓存? 
    • 如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
  • mybaits提供一级缓存,和二级缓存。 
  •  

一级缓存

    • 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
    •  

工作原理 

      • 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
      • 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
      • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
      •  

测试:

      •  

//mybatis默认支持一级缓存,不需要在配置文件去配置。

    @Test

    public void testCache1() throws Exception{

        SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //下边查询使用一个SqlSession

        //第一次发起请求,查询id为1的用户

        User user1 = userMapper.findUserById(1);

        System.out.println(user1);//      如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

        //更新user1的信息

        user1.setUsername("测试用户22");

        userMapper.updateUser(user1);

        //执行commit操作去清空缓存

        sqlSession.commit();

        //第二次发起请求,查询id为1的用户

        User user2 = userMapper.findUserById(1);

        System.out.println(user2);

        sqlSession.close();

 

    }

        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
      •  

一级缓存应用

        •  

正式开发,是将mybatis和spring进行整合开发,事务控制在service中。 
一个service方法中包括 很多mapper方法调用。

        •  

service{

    //开始执行时,开启事务,创建SqlSession对象

    //第一次调用mapper的方法findUserById(1)

 

    //第二次调用mapper的方法findUserById(1),从一级缓存中取数据

    //方法结束,sqlSession关闭

}

          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

如果是执行两个service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

  •  

二级缓存

    • 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
    • 原理 
      • 首先开启mybatis的二级缓存。
      • sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
      • 如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。
      • sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
      • 二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
      • UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
      • 每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
    • 开启二级缓存: 
      • mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。 
         
    • 调用pojo类实现序列化接口 
    • 测试 

     // 二级缓存测试

    @Test

    public void testCache2() throws Exception {

        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        SqlSession sqlSession3 = sqlSessionFactory.openSession();

        // 创建代理对象

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);

        // 第一次发起请求,查询id为1的用户

        User user1 = userMapper1.findUserById(1);

        System.out.println(user1);

 

        //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域

        sqlSession1.close();

 

        //使用sqlSession3执行commit()操作

        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);

        User user  = userMapper3.findUserById(1);

        user.setUsername("张明明");

        userMapper3.updateUser(user);

        //执行提交,清空UserMapper下边的二级缓存

        sqlSession3.commit();

        sqlSession3.close();

 

        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        // 第二次发起请求,查询id为1的用户

        User user2 = userMapper2.findUserById(1);

        System.out.println(user2);

 

        sqlSession2.close();

    }

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
  •  

useCache配置

    • 在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。 
      <select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

  •  

刷新缓存(就是清空缓存)

    • 在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
    • 设置statement配置中的flushCache=”true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。 
      如下: 
      <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

  •  

mybatis整合ehcache

    • ehcache是一个分布式缓存框架。
    • 分布缓存 
      • 我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式) 
    • 不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理
    •  

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

      •  

整合方法(掌握):

        • mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。例如: mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类。
        • 加入ehcache包 
           
          记得添加到class
        • 配置mapper中cache中的type为ehcache对cache接口的实现类型。 
        •  

在config目录下加入ehcache的配置文件

        •  

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <defaultCache            maxElementsInMemory="1000"

            maxElementsOnDisk="10000000"

            eternal="false"

            overflowToDisk="false"

            timeToIdleSeconds="120"

            timeToLiveSeconds="120"

            diskExpiryThreadIntervalSeconds="120"

            memoryStoreEvictionPolicy="LRU">

    </defaultCache></ehcache>

          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
  •  

二级缓存应用场景: 
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。 
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

  •  
  •  

二级缓存局限性 
mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

  •  

12、Mybatis和Spring整合(掌握)

  • 整合思路 
    • 需要spring通过单例方式管理SqlSessionFactory。
    • spring和mybatis整合生成代理对象,使用SqlSessionFactory创建SqlSession。(spring和mybatis整合自动完成)
    • 持久层的mapper都需要由spring进行管理。
  • 整合环境 
    • 创建一个新的java工程(接近实际开发的工程结构)
    • jar包: 
      • mybatis3.2.7的jar包
      • spring3.2.0的jar包
      • mybatis和spring的整合包:早期ibatis和spring整合是由spring官方提供,mybatis和spring整合由mybatis提供。 
         
        项目源码见链接: 密码: vcae

13、Mybatis逆向工程

  • 什么是逆向工程 
    • mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的代码(mapper.java,mapper.xml、po..)
    • 企业实际开发中,常用的逆向工程方式:由于数据库的表生成java代码。

mybatis的项目源码密码: b54x 
mybatis的文档教程密码: pbvf 
mybatis+springMVC的视频教程mybatis+springMVC

 

猜你喜欢

转载自blog.csdn.net/jokeMqc/article/details/81483792