SSM框架系列学习总结5之Mybatis实现基本CRUD和代理开发模式

首先, 回顾Mybatis的入门使用方法:
1. 创建Java工程
2. 导入相关jar包
jar.png

其中, 需要数据库驱动包, Mybatis核心包, 包括JUnit单元测试所需的jar包 hamcrest-corejunit-4.12
日志格式输出的jar包 log4jcommons-logging

  1. 准备Mybatis的全局配置文件
    具体内容见上一篇整理的博客!
  2. 准备Mybatis的映射文件
    在映射文件中编写sql语句
<!--在映射文件中编写sql语句-->
<insert></insert>
<update></update>
<delete></delete>
<select id=""  parameterType="" resultType=""></select>

<!--
id: statement的id
ParmeterType: 执行sql语句输入参数的类型
ResultType: 输出的参数类型
-->

单元测试程序:
单元测试.png

SqlSession对象的获取和使用:

SqlSessionFactoryBuilder  --->  SqlSessionFactory ---> SqlSession
SqlSessionFactoryBuilder : build(InputStream in) 获取SqlSessionFactory
SqlSessionFactory :    openSession()    获取SqlSession 

SqlSession:
selectOne(String id , Object param);
selectList(String id ,Object param);
insert()
delete()
update()

CRUD

增加

添加用户信息
这里提供了操作的实体类User.java:

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // getter and setter, toString() 省略
}

添加.png

<!-- 添加用户信息 增删改没有输出参数
        parameterType: 因为要插入的参数是一个User对象, 所以类型就是User
        #{}: 占位符中填写User的属性
        这个取值跟EL表达式取值不一样, 这里就是成员变量的变量名或getXxx()的后面一部分中的其中一个

        如何将主键的值会写到User对象中(主键自增情况下的回写)
        <selectKey>: 用来将主键回写
        keyProperty: 查询出来的主键对应着user对象的哪个属性
        order: 查询主键语句在插入语句的前面还是后面执行
        resultType: 查询出来的主键的类型

        UUID生成的主键回写

        -->
    <insert id="insertUser" parameterType="com.wtu.entity.User">
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select last_insert_id()
        </selectKey>

        <!-- 在插入语句之前, 先执行UUID() 函数, 生成一个主键, 然后主键设置到user对象中,
         然后将user对象插入数据库, 所以order="BEFORE" -->
        <!--<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">-->
            <!--select uuid()-->
        <!--</selectKey>-->

        insert into `user` values(#{id}, #{username}, #{birthday}, #{sex}, #{address})
    </insert>

单元测试代码:

    @Test
    public void insertUser() throws Exception {
        // 得到全局配置文件的输入流对象
        InputStream in = Resources.getResourceAsStream("mybatis/sqlMapConfig.xml");
        // 得到Session工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

        // 得到SqlSession
        SqlSession session = factory.openSession();

        // 创建User对象
        User user = new User();
        user.setUsername("小阳");
        user.setBirthday(new Date());
        user.setSex("1");
        user.setAddress("湖北武汉");
        // 调用的是session的insert()方法
        /*
            还传入的参数是一个User对象
            如果是增删改, 一定要记得提交事务

         */
        session.insert("demo.insertUser", user);
        System.out.println(user);
        session.commit();

        // 关闭session
        session.close();
    }
查询

分多种查询

    <select id="findAllUser" resultType="com.wtu.entity.User">
        select * from `user`
    </select>

    <!--
        id: 代表这条sql语句, 在SqlSession调用方法的时候, 需要该id作为参数进行传递, 在整个文件中不能重复
        parameterType: 参数的类型
        resultType:User
        #{}:这是个占位符, 如果参数是简单类型(基本类型+String), 那么里面可以随便写.
     -->
    <select id="findUserById" parameterType="int" resultType="com.wtu.entity.User">
        select * from `user` where id = #{id}
    </select>

    <!-- 根据姓名模糊查询
    resultType: 查询结果中, 单条结果集映射的数据类型
    parameterType: 输入参数类型
    ${} : 不是占位符, 它是拼接字符串的一个连接符, 如果传入的参数是简单类型,
     那么接收的变量只能是value, 它会将接收的参数原封不动, 不加任何修饰地拼接到SQL语句中 -->
    <select id="findUserByName" parameterType="java.lang.String"
            resultType="com.wtu.entity.User">
        select * from `user` where username like '%${value}%'
    </select>

这里的单元测试略, 和上面类似.

映射文件:

    <!-- 修改用户信息 -->
    <update id="updateUser" parameterType="com.wtu.entity.User">
        update `user` set username = #{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} where id=#{id}
    </update>

删除

相关映射文件:

    <!-- 根据id删除用户 -->
    <delete id="deleteById" parameterType="java.lang.Integer">
        delete from user where id = #{abc}
    </delete>

注意事项:

// 如果查询结果集中有多条记录, 那么使用selectList(String id, Object param)
    /*
        如果这时使用selectOne, 那么这时会抛出
        org.apache.ibatis.exceptions.TooManyResultsException:
        Expected one result (or null) to be returned by selectOne(), but found: 3
        异常
     */
映射文件SQL语句书写小结

parameterType: 输入参数类型, 一定是类型的全名, 或者是别名(int)
resultType: 输出参数类型
#{}: 占位符, 如果是简单类型, 那么里面可以随便写
${}: 是连接符, 如果是简单类型, 那么里面只能是value
如果parameterType是自定义类的类型, 那么#{}, ${}里面都要写该类型的属性名

Mybatis开发Dao层

SqlSession对象: 该对象是线程不安全的, 所以不能定义为成员变量, 只能定义局部变量.
SqlSessionFactory: 该对象在项目中只需要一个, 那么该变量可以定义为成员变量

Dao 接口
接口.png

Dao的实现类:

public class UserDaoImpl implements UserDao {
    private SqlSessionFactory factory;

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

    @Override
    public void addUser(User user) throws Exception {
        SqlSession session = factory.openSession();
        session.insert("demo.insertUser", user);
        session.commit();
        session.close();
    }

    @Override
    public void deleteUser(Integer id) throws Exception {
        SqlSession session = factory.openSession();
        session.delete("demo.deleteById", id);
        session.commit();
        session.close();
    }

    @Override
    public void updateUser(User user) throws Exception {
        SqlSession session = factory.openSession();
        session.update("demo.updateUser", user);
        session.commit();
        session.close();
    }

    @Override
    public User findUserById(Integer id) throws Exception {
        // 获取SqlSession对象
        SqlSession session = factory.openSession();
        User user = session.selectOne("demo.findUserById", id);
        return user;
    }

    @Override
    public List<User> findAllUser() throws Exception {
        SqlSession session = factory.openSession();
        List<User> userList = session.selectList("demo.findAllUser");
        return userList;
    }
}

测试类:

public class UserDaoImplTest {

    private SqlSessionFactory factory;

    // 用来获取工厂
    @Before
    public void getFactory() throws Exception {
        this.factory = new SqlSessionFactoryBuilder().
                build(Resources.getResourceAsStream("mybatis/sqlMapConfig.xml"));
    }

    @Test
    public void testFindUser() throws Exception {
        // 获取UserDao对象
        UserDao userDao = new UserDaoImpl(factory);
        User user = userDao.findUserById(28);
        System.out.println(user);
    }

    @Test
    public void testFindAllUser() throws Exception {
        // 获取UserDao对象
        UserDao userDao = new UserDaoImpl(factory);
        List<User> userList = userDao.findAllUser();
        System.out.println(userList);
    }

    @Test
    public void testAddUser() throws Exception {
        // 获取UserDao对象
        UserDao userDao = new UserDaoImpl(factory);
        User user = new User();
        user.setUsername("小花");
        user.setSex("2");
        user.setBirthday(new Date());
        user.setAddress("武汉");
        userDao.addUser(user);

        System.out.println(user);
    }

    @Test
    public void deleteUser() throws Exception {
        // 获取UserDao对象
        UserDao userDao = new UserDaoImpl(factory);
        userDao.deleteUser(29);

    }

    @Test
    public void updateUser() throws Exception {
        // 获取UserDao对象
        UserDao userDao = new UserDaoImpl(factory);
        User user = new User();
        user.setId(28);
        user.setUsername("小阳");
        user.setSex("2");
        user.setBirthday(new Date());
        user.setAddress("武汉");
        userDao.updateUser(user);

        System.out.println(user);
    }
}

Mapper代理模式开发DAO

上面的dao传统开发模式有两个不太好的地方:
1. 我们需要在每个dao方法中去获取SqlSession对象, 并且调用SqlSession中的方法, 在每个方法中都是差不多的.
2. 在每次调用SqlSession方法的时候, 传入的第一个参数都是硬编码, 写死了, 不好!!!

由此, 我们引入Mapper代理模式开发dao层
因为在每个dao的实现类的方法中, 存在大量的模板代码, 那么这些代码, 可以通过一个代理对象来调用. 但是这个代理对象根据dao的接口来生成, 所以在Mapper代理开发中, 我只需要编写dao的接口, 不需要编写其实现类.

1. 准备映射文件

如果采用的mapper代理开发模式, 那么映射文件的命名空间必须是
Mapper接口的全路径
全路径.png

2. 编写接口中的方法

三个特点:
1. Mapper接口中的方法名必须和映射文件中的sql语句的id一致
2. Mapper接口中的方法的返回值类型必须和resultType一致, 如果返回的
结果有多条记录, 那么返回值是List, 但是元素类型和resultType一致
3. Mapper接口中的方法参数必须和映射文件中parameterType一致
见代码:

/**
 * mapper开发的接口
 * Created by menglanyingfei
 * on 2018/1/20.
 */
public interface UserMapper {
    List<User> findUserByName(String name);
}
<?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代理开发模式, 那么映射文件的命名空间必须是
Mapper接口的全路径 -->
<mapper namespace="com.wtu.mapper.UserMapper">
    <!-- 根据姓名进行模糊查询  -->
    <select id="findUserByName" parameterType="java.lang.String"
            resultType="com.wtu.entity.User">
        select * from `user` where username like '%${value}%'
    </select>


</mapper>

测试代码:

public class MapperTest {
    private SqlSessionFactory factory;

    @Before
    public void getFactory() throws Exception {
        this.factory = new SqlSessionFactoryBuilder().
                build(Resources.getResourceAsStream("mybatis/sqlMapConfig.xml"));
    }

    @Test
    public void testFindUserByName() {
        // 获取SqlSession
        SqlSession session = factory.openSession();
        // 通过SqlSession对象得到Mapper接口的一个代理对象
        // 需要传递的参数是Mapper接口的类型
        UserMapper userMapper = session.getMapper(UserMapper.class);
        // 通过代理对象调用UserMapper中的方法
        List<User> userList = userMapper.findUserByName("小");
        System.out.println(userList);
        // 红色标识!
        System.err.println(userMapper);
        // org.apache.ibatis.binding.MapperProxy@289d1c02
        // 关闭资源
        session.close();
    }

Mybatis全局配置文件的一些配置信息

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

注意:上面几项配置, 必须按照顺序进行配置

settings(全局配置参数)

该配置中可以配置全局参数, 比如二级缓存, 延迟加载和日志信息的配置
配置信息参考: mybatis-settings.xlsx(包含在下面的代码文件夹中)

    <!-- 在控制台输出日志信息 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
properties: 一般用来配置数据库的连接信息

在properties标签体, 还可以通过property标签来设置属性, 但是此时的加载顺序是先加载的是property标签中的属性, 然后再加载resource指定的外部属性文件中的属性.

    <!-- 加载数据库配置信息的属性文件, 跟spring整合以后就没了 -->
    <properties resource="dbConfig.properties">
        <!--<property name="user" value="root1"/>-->
    </properties>
typeAliases(类型别名, 重要)

Mybatis默认支持的别名
别名.png

自定义别名:
1. 配置单个别名:
image.png

  1. 批量配置别名: mybatis扫描某一个包, 对包下的所有类都创建别名
    image.png
<!-- 配置自定义类型别名
     type: 本来的类型
     alias: 别名
     <package name=""/>: 批量扫描某一个包, 如果是批量配置别名, 那么
     别名就是类名, 并且首字母大写和小写都可以
     -->
    <typeAliases>
        <!-- 配置单个别名 -->
        <!--<typeAlias type="com.wtu.entity.User" alias="user"/>-->
        <package name="com.wtu.entity"/>
    </typeAliases>

typeHandlers(类型处理器)
一般不需要配置
image.png

补充(但使用较少!):

<!-- environments 里面的内容在mybatis和spring整合以后, 就全部没了 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClass}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
mappers(映射器)

用来在全局配置文件中加载映射文件
加载单个映射文件

<mappers>
        <!-- 将映射文件加载到全局配置文件 -->
        <mapper resource="mybatis/user.xml"/>

        <!-- 将UserMapper.xml加入到全局配置文件 -->
        <!--<mapper resource="mapper/UserMapper.xml"/>-->

        <!-- 通过mapper接口来加载映射文件
            1. 映射文件的文件名必须和接口名相同
            2. 映射文件必须和Mapper接口处于同一个目录下
            3. 这种方式只适合Mapper代理开发模式 -->
        <!--<mapper class="com.wtu.mapper.UserMapper"/>-->

        <!-- 批量加载映射文件, 自动去扫描某一个包, 将该包下的所有映射文件加载, 推荐使用! -->
        <package name="com.wtu.mapper"/>
    </mappers>

完!
完整代码地址见:
https://github.com/menglanyingfei/SSMLearning/tree/master/mybatis_day02

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值