当前位置:   article > 正文

SSM框架学习----Mybatis(2)_userinterface接口,包括实现数据库的模糊查询,插入,增加,删除的抽象方法。

userinterface接口,包括实现数据库的模糊查询,插入,增加,删除的抽象方法。

Mybatis框架学习----(2)

1. Mybatis的自定义分析

1. 第一步

SqlSessionFactoryBuilder接受SqlMapConfig.xml的文件流,构建出SqlSessionFactory对象

2. 第二步

SqlSessionFactory读取SqlMapConfig.xml中连接数据库和mapper的映射信息,用来生产出真正操作数据库的SqlSession对象

3. 第三步

SqlSession对象的两大作用:1) 生产接口代理对象;2) 定义通用增删改查方法

注意:两者除了获取数据库信息,还需要得到sql语句

4. 第四步

作用1)

在SqlSessionImpl对象中(SqlSession的实现类对象)的getMapper方法分为两步骤:1) 用SqlSessionFactory读取数据库连接信息创建Connection对象;2) 通过jdk代理模式创建出代理对象作为getMapper方法的返回值,主要工作是在创建对象时第三个参数InnovationHandler接口的重写处理类里面得到sql语句,执行对象的CURD操作

作用2)

在SqlSessionImpl对象中提供selectList方法[其实在Mybatis框架中还有selectOne,insert等方法,同样分为两步走]:1) 用SqlSessionFactory读取的数据库连接信息创建出JDBC的Connection对象;2) 直接得到Sql语句,使用JDBC的Connection对象进行CRUD操作

5. 第五步

封装结果集:无论使用分支一生成代理对象,还是直接使用分支二提供的通用CRUD方法,都要对返回的数据库结果进行封装,变成java对象返回给调用者,所以需要知道调用者所需的返回值类型(封装成什么类的对象)

总结: 无论是让Mybatis帮助创建代理对象还是直接使用Mybatis提供的CRUD方法,其本质都是得到JDBC的Connection对象,执行对应的sql语句,最终封装到结果集。只是注解和xml配置两种开发模式在传递sql语句和返回值类型的方式上有所差异:

  • 注解的SQL语句获取是在Dao接口方法名上的@Select注解中,返回值类型根据方法名的返回值类型来获取
  • xml配置方式:在Dao接口的xml配置中,从resultType中获取返回值类型,并且获取其sql语句

2. Mybatis实现CRUD操作

1. 在之前的入门案例中的Dao接口中增加一个saveUser的抽象方法

/**
 * 用户的持久层接口
 *
 */
public interface UserInterface {

    /**
     * 查询所有用户操作
     * @return
     */
    //@Select("select * from account") // 对于使用注解来配置UserDao
    List<User> findAll();

    void saveUser(User user); // 新增用户方法

    void updateUser(User user); // 更新用户方法

    void deleteUser(Integer UserId);  // 删除用户方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2. 在Dao接口的XML配置文件中,增加更新的sql语句

3. 在Dao接口的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名称空间,Dao接口的全类名,告诉mybatis这个配置文件是实现哪个接口的-->
<mapper namespace="com.huzhen.domain.dao.UserInterface">
    <!-- 配置查询表user所有记录的sql语句 并且加上resultType返回值类型,sql语句的查询结果集将会被封装到
    User类中,最终返回一个List<User>列表-->
    <select id="findAll" resultType="com.huzhen.domain.User">
        select * from account;
    </select>

    <insert id="saveUser" parameterType="com.huzhen.domain.User">
        insert into account(username, birthday, sex, addres) values(#{username},
                    #{birthday}, #{sex}, #{addres});
    </insert>

    <update id="updateUser" parameterType="com.huzhen.domain.User">
        update account set username=#{username}, birthday=#{birthday}, sex=#{sex},
                    addres=#{addres} where id=#{id};
    </update>

    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from account where id=#{Uid}
    </delete>
</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

注意:在JDBC中可以使用?来进行占位符,

insert into account values(?, ?, ?, ?)
  • 1

而Mybatis中是通过增加**parameterType参数用来指定传递进来的参数是User类型下的参数,并且只有使用jdk自带的getter和Setter方法生成的获取参数和返回参数方法,才能使用#{}**来传递参数,否则需要指定get和Set的方法名而不是成员变量名。在删除的配置中,传入的parameterType中不再是自定义的封装类User而是根据什么来查询,这里根据id来查询,则传入进去的是Integer整型,如果是根据username或者其他的可以传入String类型。

  • parameterType和resultType的区别在于:如果该sql语句有输入,则指明parameterType,有输出则指明resultType

  • 在test包下增加一个用于测试的类,由于安装的依赖是Junit版本是5.8.2,不能再使用注解==@BeforeAll和@AfterAll>==

而只能使用@BeforeAll和BeforeAll,并且这两个注解只能修饰静态方法,所以在成员变量处也需要设置static修饰符。之前对于读取配置文件,获取SqlSessionFactory来创建SqlSession对象,获取Dao接口的代理对象以及释放资源是单个写的,现在对于多个测试案例,可以将共用方法进行封装到init和close方法中,需要注意的是对于增加,删除和更新操作时,需要进行事务提交才能正常运行。

4. test类中进行增删改的测试

public class test {
    /**
     * 由于需要测试多个操作,而每个test中读取配置文件,获取SqlSessionFactory来创建对象SqlSession
     * 以及获取Dao的代理对象和释放资源都是共用的,所以可以封装在一个init方法中
     */
    private static InputStream is;
    private static SqlSession sqlSession;
    private static UserInterface userDao;

    @BeforeAll
    public static void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(UserInterface.class);
    }
    // AfterAll和BeforeAll注解修饰的必须是静态方法
    @AfterAll
    public static void close() throws IOException{
        sqlSession.commit(); // 对于更新,插入,删除操作必须有提交事物才能正常运行
        sqlSession.close();
        is.close();
    }
    /**
     * 测试查询所有用户
     */
    @Test
    public void testFindAll(){
        List<User> list = userDao.findAll();
        for(User user : list){
            System.out.println(user);
        }
    }

    /**
     * 测试新增用户
     */
    @Test
    public void testInsertUser(){
        User user = new User();
        user.setUsername("Bin");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddres("上海虹桥");

        userDao.saveUser(user);
        // 必须需要提交事物才能添加成功,在close中执行
        System.out.println("添加成功!");
    }
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(19);
        user.setUsername("A Bin");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddres("上海滴水湖");

        userDao.updateUser(user);
    }

    @Test
    public void testUser(){

        userDao.deleteUser(19);
    }
}
  • 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

5. 关于增加用户的SQL语句深入

新增用户后,同时需要返回当前新增用户的id值,因为id是由数据库的**AUTO_INCREMENT**实现的。所以相当于在新增用户将自动增长auto_increment的值返回。

那么在XML中的insert标签中需要加入select last_insert_id()语句:

    <insert id="saveUser" parameterType="com.huzhen.domain.User">
        <!-- keyProperty表示返回的值名称对应实体类USer keyColumn对应表中的id order取值为after表示插入后的行为  -->
        <selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="INT">
            select last_insert_id();
        </selectKey>
        insert into account(username, birthday, sex, addres) values(#{username},
        #{birthday}, #{sex}, #{addres});
    </insert>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其中,**keyProperty表示返回值名称对应的是实体类User中的id,而KeyColumn对应的是表中的字段id,order表示下面的语句是在插入前执行还是在插入后执行,这里赋值AFTER**表示在查询之后返回id值

在test类中进行测试代码如下:

    @Test
    public void testInsertUser(){
        User user = new User();
        user.setUsername("Bin");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddres("上海虹桥");
        System.out.println("插入用户前:" + user);
        userDao.saveUser(user);
        // 必须需要提交事物才能添加成功,在close中执行
        System.out.println("添加成功!");
        System.out.println("插入用户后:" + user);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出结果为:

请添加图片描述
可以看出插入前后id的改变

6. 根据Id查询和模糊查询, 聚合函数查询

和上面一样,首先在Dao接口中定义方法名,其次在XML配置文件中加入sql语句,最后在测试类中进行测试:

public interface UserInterface {

    /**
     * 定义sql语句的方法
     * @return
     */
    //@Select("select * from account") // 对于使用注解来配置UserDao
    List<User> findAll();     // 查询所有用户

    void saveUser(User user); // 新增用户方法

    void updateUser(User user); // 更新用户方法

    void deleteUser(Integer UserId);  // 删除用户方法

    User findById(Integer userId);   // 根据id查询用户信息

    List<User> findByName(String userName); // 根据用户名模糊查询

    int findTotalUser();  // 查询用户的总记录数
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
<select id="findById" parameterType="INT" resultType="com.huzhen.domain.User">
        select * from account where id=#{Uid};
    </select>

    <select id="findByName" parameterType="String" resultType="com.huzhen.domain.User">
        select * from account where username like #{name};
      <!-- select * from account where username like '%${value}%' -->
    </select>
    
    <select id="findTotalUser" resultType="java.lang.Integer">
        select count(id) from account;
    </select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 对于查询单个用户的这里是根据id来查询,所以parameterType的参数为int,返回是一个User对象,在测试类中:
    @Test
    public void testSelectById(){

        User user = userDao.findById(2);
        System.out.println(user);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

查询结果为:
请添加图片描述

  • 对于模糊查询,分为两种情况

    • i ) 在xml中不设置%模糊查询,select * from account where username like #{name}; 在测试类中进行模糊查询的操作:List<User> list = userDao.findByName("%User%");
    • ii) 另一种则是在xml设置模糊查询,使用**’% v a l u e {value}%'</span>**:`select * from account where username like '% value{value}%', 而在测试类中则无需在使用模糊查询了:List list = userDao.findByName(“User”);`

    但是两者存在区别:对于i) 读取到的sql语句为:select * from account where username like '%王%';而对于ii) 读取到的sql语句是:select * from account where username like ? 配置文件的模糊查询得到的是Statement对象的字符串拼接SQl,而测试类的模糊查询得到的是PreparedStatement的参数占位符,因此不推荐在配置文件中使用模糊查询,易引起sql注入问题

查询结果为:
请添加图片描述

  • 对于聚合函数的查询,由于查询的是用户的总记录数,因此在返回值类型resultType中设置为INT或者java.lang.Integer均可,在测试类:
    @Test
    public void testTotalUserNumber(){
        int number = userDao.findTotalUser();
        System.out.println("总记录数:" + number);
    }
  • 1
  • 2
  • 3
  • 4
  • 5

查询结果为:
请添加图片描述

3. Mybatis的参数深入

3.1. Mybatis的参数

3.1.1. ParameterType输入类型
3.1.2. 传递简单类型
  • 传递INT类型的id
  • 传递模糊查询的String username
  • 传递实体类型的User
3.1.3. 传递pojo对象

Mybatis使用**OGNL**表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称

OGNL:Object Graphic Navigation Languag 对象图导航语言,通过对象的取值方法来获取数据,在写法上将get给省略了,如:获取用户名称


在USer类中:user,getUsername();

OGNL表达式写法:user.username();


Mybatis中为什么可以直接写username,而不使用 user. ?

因为在parameterType中已经提供了属性所属的类,如:com.huzhen.domain.User,所以不再需要写对象名

3.1.4. 传递pojo包装对象

开发中通过pojo传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其他的查询条件(如将用户购买商品信息也作为查询条件),此时可以使用包装对象传递输入参数。

Pojo类中包含pojo

需求:根据用户名来查询用户信息,查询条件放到QueryVo的user属性中

目的:将实体类对象user再封装一层,适用于由多个实体类对象组合成一个查询条件来实现数据的查询

在Dao接口中增加一个方法:

 // 根据queryVo中的条件查询用户: 将实体类对象再封装一层,应用在由多个对象组成一个查询条件来实现数据的查询
    List<User> findUserByVo(QueryVo vo);
  • 1
  • 2

这里的QueryVo类需要自己定义,由于这里只涉及到一个实体类对象,那么成员变量只有一个user,在domain包下定义:

public class QueryVo {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

进而在XML文件中进行配置,注意这里的查询返回值不能再是User,由于传递进去的是vo对象,所以应该是:

    <!--  根据queryVo的条件查询用户  -->
    <select id="findUserByVo" parameterType="com.huzhen.domain.QueryVo" resultType="com.huzhen.domain.User">
        select * from account where username like #{user.username};
    </select>
  • 1
  • 2
  • 3
  • 4

另外,在模糊查询中的#{}参数不能直接用username,因为在queryVo类中找不到username这个属性,应该由vo对象的属性user来找到username: #{user.username}.

3.2. Mybatis的输出结果封装(resultType)

3.2.1. 输出简单类型
  • 输出的可以是int类型,如:select count(id) from account;
  • 输出的可以是User类型,如:select * from account;

==注意:==输出简单类型必须查询出来的结果集只有一条记录,最终将第一个字段的值转换为输出类型,使用session的selectOne可查询单条记录。

3.2.2. 输出pojo类型
3.2.3. 输出pojo列表

注意:当查询封装结果集时,要求实体类User的属性必须与数据库中的列名保持一致,如果不一致,那么,需要修改的地方有:

public class User implements Serializable {

    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddres;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 测试类中调用get和set的方法名需要更改
  • xml文件中关于增删改中,keyProperty的属性值不再是id而是userId,另外,在values中会找对应的OGNL表达式,以前的#{id},#{birthday}找不到了,需要改成实体类中对应的属性名==#{userId},#{userBirthday}==
<insert id="saveUser" parameterType="com.huzhen.domain.User">
    <!-- keyProperty表示返回的值名称对应实体类USer keyColumn对应表中的id order取值为after表示插入后的行为  -->
    <selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="INT">
        select last_insert_id();
    </selectKey>
    insert into account(username, birthday, sex, addres) values(#{username},
    #{birthday}, #{sex}, #{addres});
</insert>

<update id="updateUser" parameterType="com.huzhen.domain.User">
    update account set username=#{username}, birthday=#{birthday}, sex=#{sex},
                addres=#{addres} where id=#{id};
</update>

<delete id="deleteUser" parameterType="java.lang.Integer">
    delete from account where id=#{Uid};
</delete>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 对于查询语句findAll,并不会正确的返回用户列表,返回的结果为:
    请添加图片描述

除了username的属性可以得到,其他属性值均不能正确得到,因为windows中的mysql是不区分大小写的,所以将username与userName识别为同一个属性,因此可以正确返回,而其他的属性值在数据库中找不到对应的列。

那么,解决方法有:

  • 针对sql语句,对属性名起别名的方式来解决:select id as userId, birthday as userBirthday, sex as userSex, addres as userAddres from account; 这种解决方法执行效率快,但是开发过程麻烦,代码冗余
  • 在Dao接口的xml配置文件中,增加一个resultMap配置,如下:
 <resultMap id="userMap" type="com.huzhen.domain.User">
        <!--  主码的配置 property对应实体类中的属性 column对应数据库的列名      -->
        <id property="userId" column="id"></id>
        <!--   非主码的配置    -->
        <result property="userName" column="username"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
        <result property="userAddres" column="addres"></result>
    </resultMap>
    <!--  注意:当使用配置来连接实体类属性名和数据库列名时候,查询xml不能再使用resultType,而是resultMap  -->
    <select id="findAll" resultMap="userMap">
        select * from account;
    </select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

其中,id为该map的名字,type依旧是实体类User;需要注意的是:在以后的sql配置中,不能在使用resultType来进行User类型的返回值的赋值,而是采用resultMap来进行返回,对应上面定义的resultMap方法名id。该种方法开发效率高,但由于会多解析一段xml配置,因此执行效率较低。

4. Mybatis实现Dao层开发

使用Mybatis开发Dao,通常有两个方法:原始Dao开发Mapper接口代理开发。现在主流的是接口代理开发方式。

4.1. Mybatis实现Dao的传统开发方式

4.2. 自定义Dao实现类的Mybatis的执行过程

SqlSession中封装了对数据库的CRUD操作,通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder创建

自定义实现Dao的实现类来分析Mybatis是如何使用动态代理来执行sql语句的:

在Dao.impl包下创建一个Dao接口的实现类UserDaoImpl:

public class UserDaoImpl implements UserInterface{

    private SqlSessionFactory factory;

    public UserDaoImpl(SqlSessionFactory factory){
        this.factory = factory;
    }

    @Override
    public List<User> findAll() {
        SqlSession session = factory.openSession();
        // factory 获取sqlsession对象,调用其中的selectList方法返回输出列表
        // 传递的参数为: Dao接口的全限定类名.方法名
        List<User> list = session.selectList("com.huzhen.dao.UserInterface.findAll");
        session.close();
        return list;
    }

    @Override
    public void saveUser(User user) {
        SqlSession session = factory.openSession();
        // 对于insert方法需要多传递一个user对象
        session.insert("com.huzhen.dao.UserInterface.saveUser", user);
        session.commit();
        session.close();
    }

    @Override
    public void updateUser(User user) {
        SqlSession session = factory.openSession();
        session.update("com.huzhen.dao.UserInterface.updateUser", user);
        session.commit();
        session.close();
    }

    @Override
    public void deleteUser(Integer UserId) {
        SqlSession session = factory.openSession();
        session.update("com.huzhen.dao.UserInterface.deleteUser", UserId);
        session.commit();
        session.close();
    }

    @Override
    public User findById(Integer userId) {
        SqlSession session = factory.openSession();
        User user = session.selectOne("com.huzhen.dao.UserInterface.findById", userId);
        session.close();
        return user;
    }

    @Override
    public List<User> findByName(String userName) {
        SqlSession session = factory.openSession();
        List<User> list = session.selectList("com.huzhen.dao.UserInterface.findByName", userName);
        session.close();
        return list;
    }

    @Override
    public int findTotalUser() {
        SqlSession session = factory.openSession();
        Integer count = session.selectOne("com.huzhen.dao.UserInterface.findTotalUser");
        return count;

    }
}
  • 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

覆盖重写Dao接口的抽象方法,在构造函数中传递一个SqlSessionFactory对象,用来创建SqlSession对象,来执行sql语句。对于查询的结果集只有一个记录,使用Session的selectOne方法(返回一个用户,或者一个聚合函数的值总数目等),如果某个方法有输入参数,那么在调用Session的方法时需要将该参数传递,如:session.insert("com.huzhen.dao.UserInterface.saveUser", user); User user = session.selectOne("com.huzhen.dao.UserInterface.findById", userId);

在测试类中的实现如下:

public class test {
    /**
     * 由于需要测试多个操作,而每个test中读取配置文件,获取SqlSessionFactory来创建对象SqlSession
     * 以及获取Dao的代理对象和释放资源都是共用的,所以可以封装在一个init方法中
     */
    private static InputStream is;
    private static UserInterface userDao;

    @BeforeAll
    public static void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        // 使用工厂对象创建dao对象
        userDao = new UserDaoImpl(factory);

    }
    // AfterAll和BeforeAll注解修饰的必须是静态方法
    @AfterAll
    public static void close() throws IOException{
       is.close();
    }
    /**
     * 测试查询所有用户
     */
    @Test
    public void testFindAll(){
        List<User> list = userDao.findAll();
        for(User user : list){
            System.out.println(user);
        }
    }
    /**
     * 测试新增用户
     */
    @Test
    public void testInsertUser(){
        User user = new User();
        user.setUsername("Bin");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddres("上海虹桥");
        System.out.println("插入用户前:" + user);
        userDao.saveUser(user);
        // 必须需要提交事物才能添加成功,在close中执行
        System.out.println("添加成功!");
        System.out.println("插入用户后:" + user);
    }
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(21);
        user.setUsername("The shy");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddres("上海滴水湖");

        userDao.updateUser(user);
    }
    @Test
    public void testDeleteUser(){

        userDao.deleteUser(21);
    }
    @Test
    public void testSelectById(){

        User user = userDao.findById(2);
        System.out.println(user);
    }
    @Test
    public void testSelectByName(){
        List<User> list = userDao.findByName("%User%");
        for(User user : list){
            System.out.println(user);
        }
    }
    @Test
    public void testTotalUserNumber(){
        int number = userDao.findTotalUser();
        System.out.println("总记录数:" + number);
    }
}
  • 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

由于已经在Dao的实现类中定义了SqlSession对象,所以在测试类中的init函数中无需再根据factory来创建sqlSession,直接创建一个Dao接口的实现类对象来执行sql语句,不过在创建Dao对象时,需要传递一个factory对象。

通过debug,流程图:

请添加图片描述

当找到PreparedStatement对象时,才是JDBC中执行sql语句的方式,而关于PreparedStatement对象的执行方法有三种:

  • execute: 能执行CRUD中的任意一种语句,返回一个布尔值,表示是否有结果集,有结果集返回true否则返回false
  • executeUpdate: 无法执行查询语句,返回的是影响数据库记录的行数
  • executeQuery: 只能执行select语句,无法执行增删改,执行结果为封装的ResultSet对象

4.3. 代理Dao的Mybatis的执行过程

Mybatis可以使用Session.getMapper来获取Dao接口的代理对象,来帮助我们调用执行sql语句的方法,其具体流程为:

请添加图片描述请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

在进入到MapperProxyFactory类中的newInstance方法中可以看到使用了动态代理**Proxy.newProxyInstance,重点关注第三个参数mapper Proxy,是接口InvocationHandler的实现类,调用了invoke方法,而invoke方法中的execute方法中执行了insert,update,delete以及selectList**方法,这才是执行sql语句的方法。

5. SqlMapConfig.xml配置文件

5.1. 配置内容

SqlMapConfig.xml中配置的内容和顺序如下:

  • properties 属性
  • settings 全局配置参数
  • typeAliases 类型别名
  • typeHandlers 类型处理器
  • objectFactory 对象工厂
  • plugins 插件
  • environments 环境集合属性对象
    • environment 环境子属性对象
    • transactionManager 事务管理
    • dataSource 数据源
  • mappers 映射器
5.2. properties (属性)

SqlMapConfig.xml可以引用java属性文件的配置信息:

jdbc.deriver=com.mysql.cj.jdbc.Driver 
jdbc.url=mysql://localhost:3306/eesy?serverTimezone=UTC&amp;useSSL=false
jdbc.username=root
jdbc.password=root    
  • 1
  • 2
  • 3
  • 4

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>

    <properties>
        <!-- 配置连接数据库的4个基本信息:驱动, url, 用户名以及密码-->
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy?serverTimezone=UTC&amp;useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </properties>
    <!-- 配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(数据库连接池)-->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息:驱动, url, 用户名以及密码-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定映射配置文件的位置,映射配置文件即为每个dao独立的配置文件
        如果是使用注解来配置,此处应该使用class属性指定被注解的dao全限定类名即dao下面的接口的全类名-->
    <mappers>
        <mapper resource="com/huzhen/dao/UserDaoImpl.xml"></mapper>
<!--        <mapper class="com.huzhen.domain.dao.UserInterface"></mapper>-->
    </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
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

显然,在environment中的property中的value对应着properties中的name,这样做虽然可以正确执行,但是没有必要。更多的是在properties属性,可以在标签内部配置连接数据库的信息, 也可以通过属性引用外部配置文件jdbcConfig.properties:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy?serverTimezone=UTC&useSSL=false
jdbc.username=root
jdbc.password=123456
  • 1
  • 2
  • 3
  • 4
  • resources属性:用于指定配置文件的位置,是按照类路径写法,并且放在resources的类路径下。
    <!-- 引用外部配置文件信息 -->
    <properties resource="jdbcConfig.properties"></properties>
    <!-- 配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(数据库连接池)-->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息:驱动, url, 用户名以及密码-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

注意property中的value不能直接写url等,而是写jdbcConfig中对应的name。

  • url属性:要求按照url格式书写地址,包括:协议 + 主机地址 + 端口号 + URI(统一资源标识符)

window默认地址路径是file协议,如下:
请添加图片描述

所以,在XML配置文件中的url定义为:

<properties url="file:///D:/java/Mybatis_Dao/src/main/resources/jdbcConfig.properties"></properties>

同样实现和resource加载配置文件的效果,另外在mapper中读取Dao接口配置文件的方法也可以使用url地址来读取:

<mapper url="file:///D:/java/Mybatis_Dao/src/main/resources/com/huzhen/dao/UserDaoImpl.xml"></mapper>

5.3. typeAliases(domain类中的别名)

在SqlMapConfig.xml配置文件中加入typeAliases:

    <!-- 使用typeAliases配置别名,只能配置domain中类的别名  -->
    <typeAliases>
        <typeAlias type="com.huzhen.domain.User" alias="user"></typeAlias>
    </typeAliases>
  • 1
  • 2
  • 3
  • 4

其中,typeAlias用于配置别名,type属性指定的是domain下的实体类的全限定类名,alias为指定类名,当指定了别名就不再区分大小写,在sql语句中的resultType或者parameterType中就可以不用再写User的全限定类名,直接写user即可:

    <select id="findAll" resultType="user">
        select * from account;
    </select>
  • 1
  • 2
  • 3

如果,domain包下存在多个实体类,一个一个去定义比较麻烦,可以在typeAliases标签下使用package包,

<!-- 使用typeAliases配置别名,只能配置domain中类的别名  -->
<typeAliases>
    <package name="com.huzhen.domain"></package>
</typeAliases>
  • 1
  • 2
  • 3
  • 4

用于指定配置别名的包,指定之后,domain包下的所有类名均是别名,不再区分大小写

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号