JavaWeb-Mybatis框架(2)

Mybatis对数据库的单表CUDA操作

1. 配置Mybatis的主配置文件SqlMapConfig,配置数据库的基本信息以及操作的映射配置文件

<?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">
<!-- mybatis的主配置文件 -->
<configuration>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="bjtu/dao/UserDao.xml"/>
    </mappers>
</configuration>

2. 实现User类,定义类属性,和数据库表user中的列值对应

gg.png

3. 实现操作接口UserDao,定义每个操作数据库的方法

public interface UserDao {
    /*
    查询所有操作
     */
    //@Select("select * from user")
    List<user> findAll();
    /**
     * 保存user
     * @param u
     */
    void saveUser(user u);
    /**
     * 更新信息
     * @param u
     */
    void updateUser(user u);
    /**
     * 删除用户
     * @param userID
     */
    void deleteUser(Integer userID);
    /**
     * 按ID查询用户信息
     * @param userID
     */
    user findById(Integer userID);
    /**
     * 按名称模糊查询用户信息
     */
    List<user> findByName(String username);
    /**
     * 聚合函数:查询总记录条数
     */
    int findTotal();
}

4. 将UserDao中的每个方法在映射配置文件UserDao.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="bjtu.dao.UserDao">
    <!--配置查询结果的列名与实体类的属性名的对应关系-->
    <resultMap id="userMap" type="bjtu.domain.user">
        <!-- 主键字段的对应-->
        <id property="userId" column="id"></id>
        <!-- 非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userBirthday" column="birthday"></result>
        <result property="userSex" column="sex"></result>
        <result property="userAddress" column="address"></result>
    </resultMap>

    <!--配置查询所有-->
    <select id="findAll" resultMap="userMap">
        select * from user
    </select>
    <!--插入保存用户-->
    <insert id="saveUser" parameterType="bjtu.domain.user">
        <!--配置插入数据后,获取新增数据的id-->
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})
    </insert>
    <!--更新用户信息-->
    <update id="updateUser" parameterType="bjtu.domain.user">
        update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
    </update>
    <!--删除用户-->
    <delete id="deleteUser" parameterType="int">
        delete from user where id=#{uid}
    </delete>
    <!--按ID查询一个用户的信息-->
    <select id="findById" parameterType="int" resultMap="userMap">
        select * from user where id=#{uid}
    </select>
    <!--按名称模糊查询用户信息-->
    <select id="findByName" parameterType="string" resultMap="userMap">
        select * from user where username like #{name}
    </select>
    <!--利用聚合函数查询总记录条数-->
    <select id="findTotal" resultType="int">
        select count(id) from user
    </select>
</mapper>

其中 resultMap 是用来将User类中的属性名和mysql中user表中的类名对应:

<resultMap id="userMap" type="bjtu.domain.user">
        <!-- 主键字段的对应-->
        <id property="userId" column="id"></id>
        <!-- 非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userBirthday" column="birthday"></result>
        <result property="userSex" column="sex"></result>
        <result property="userAddress" column="address"></result>
</resultMap>

5. 在test目录下写测试程序 MybatisTest.java

public class MybatisTest {
    private InputStream in;
    private SqlSession sqlSession;
    private UserDao userDao;

    @Before
    public void init() throws Exception{
        //1. 读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. 获取SqlFactorySession
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3. 获取SqlSession对象
        sqlSession = factory.openSession();
        //4. 获取dao的代理对象
        userDao = sqlSession.getMapper(UserDao.class);
    }

    @After
    public void destory() throws Exception{
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    /*
    测试查询所有数据功能
     */
    @Test
    public void testFindAll() throws Exception{
        List<user> users = userDao.findAll();
        for(user u: users){
            System.out.println(u);
        }
    }

    /*
    测试保存功能
     */
    @Test
    public void testSave() throws Exception{
        user u = new user();
        u.setUserName("李明");
        u.setUserAddress("北京海淀");
        u.setUserSex("男");
        u.setUserBirthday(new Date());
        System.out.println("Before saving:"+u);
        userDao.saveUser(u);
        System.out.println("After saving:"+u);
    }

    /**
     * 测试更新信息功能
     */
    @Test
    public void testUpdate(){
        user u = new user();
        u.setUserId(49);
        u.setUserName("Mybatis");
        u.setUserAddress("bjtu");
        u.setUserSex("m");
        u.setUserBirthday(new Date());

        userDao.updateUser(u);
    }

    /**
     * 测试删除功能
     */
    @Test
    public void testDelete(){
        userDao.deleteUser(57);
    }

    /**
     * 按ID查询一个用户的信息
     */
    @Test
    public void testFindOne(){
        user u = userDao.findById(48);
        System.out.println(u);
    }

    /**
     * 按名称模糊查询用户的信息
     */
    @Test
    public void testFindByName(){
        List<user> users = userDao.findByName("%王%");
        for(user u: users){
            System.out.println(u);
        }
    }

    /**
     * 利用聚合函数查询总记录的条数
     */
    @Test
    public void testFindTotal(){
        int s = userDao.findTotal();
        System.out.println("总记录条数:"+s);
    }
}

Mybatis连接池和事务

  1. 事务

    在Mybatis的SqlMapConfig.xml配置文件中,通过来实现Mybatis中连接池的配置。

    type属性的三种取值:

    • UNPOOLED 不使用连接池的数据源
    • POOLED 使用连接池的数据源
    • JNDI 使用JNDI实现的数据源

      MyBatis内部分别定义了实现了java.sql.DataSource接口的UnpooledDataSource,PooledDataSource类来表示UNPOOLED、POOLED类型的数据源。

  2. 事务

    什么是事务?
    事务的四大特性:ACID
    不考虑隔离就产生的三个问题
    解决方法:四种隔离级别

Mybatis多表查询

1. 数据库创建表

在数据库中创建user、account和role两个表,并设置外键关联。

1)创建user表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` char(1) default NULL COMMENT '性别',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');

2) 创建account表
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);

3)创建role表
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `ROLE_NAME` varchar(30) default NULL COMMENT '角色名称',
  `ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
  PRIMARY KEY  (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');


DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `UID` int(11) NOT NULL COMMENT '用户编号',
  `RID` int(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user_role`(`UID`,`RID`) values (41,1),(45,1),(41,2);DROP TABLE IF EXISTS `role`;

1.JPG

2. 一对一和一对多查询

本次案例主要以最为简单的用户和账户的模型来分析Mybatis 多表关系。 用户为User 表,账户为Account表。一个用户(User)可以有多个账户(Account)。

1.JPG

准备:
1. 建立两张表:用户表(User)和账户表(Account)  
    让用户和账户表之间具有一对多的关系,需要外键在账户表中添加
2. 简历两个实体类:用户实体类和账户实体类
     让用户和账户实体类中体现一对多的关系
3. 建立两个配置文件:
    用户配置文件
    账户配置文件
4. 实现配置
    当查询用户时,可以同时得到用户下所包含的账户信息
    当查询账户时,可以同时得到账户所属的用户信息
  • 账户一对一对应用户,AccountDao.xml查询配置文件
    1to1.JPG

  • 用户一对多用户
    1)首先要在User类中增加user与account一对多关系,即一个用户下有多个accounts,用集合封装。
    reflection.JPG
    2)其次修改UserDao.xml查询配置文件
    1tom.JPG

3. 多对多查询

多对多.JPG

示例描述

示例:用户和角色
            一个用户可以有多个角色
            一个角色可以赋予多个用户
步骤:
    1、建立两张表:用户表,角色表
        让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
    2、建立两个实体类:用户实体类和角色实体类
        让用户和角色的实体类能体现出来多对多的关系
        各自包含对方一个集合引用
    3、建立两个配置文件
        用户的配置文件
        角色的配置文件
    4、实现配置:
        当我们查询用户时,可以同时得到用户所包含的角色信息
        当我们查询角色时,可以同时得到角色的所赋予的用户信息

这个多对多的搜索主要是修改SQL语句,映射文件.xml的配置和一对多查询操作类似

* UserDao.xml配置文件,RoleDao.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="bjtu.dao.RoleDao">
    <!--定义User的resultMap-->
    <resultMap id="roleMap" type="bjtu.domain.Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="bjtu.domain.User">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>

        </collection>

    </resultMap>
    <!--配置查询所有-->
    <select id="findAll" resultMap="roleMap">
        select u.*, r.id as rid, r.role_name, r.role_desc from role r
        left outer join user_role ur on r.id = ur.rid
        left outer join user u on u.id = ur.uid
    </select>

</mapper>

Mybatis的缓存

  1. Mybatis中的延迟加载

    问题:在一对多中,当我们有一个用户,它有100个账户。
          在查询用户的时候,要不要把关联的账户查出来?
          在查询账户的时候,要不要把关联的用户查出来?
    
          在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。
          在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
    
    • 什么是延迟加载?

      在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)

    • 什么是立即加载?

      不管用不用,只要一调用方法,马上发起查询。

      在对应的四种表关系中:一对多,多对一,一对一,多对多。 一对多,多对多:通常情况下我们都是采用延迟加载; 多对一,一对一:通常情况下我们都是采用立即加载。

  2. Mybatis中的缓存

    • 什么是缓存

      存在于内存中的临时数据。

    • 为什么使用缓存

      减少和数据库的交互次数,提高执行效率。

    • 什么样的数据能使用缓存,什么样的数据不能使用

      适用于缓存:  
          经常查询并且不经常改变的。
          数据的正确与否对最终结果影响不大的。
      不适用于缓存:
          经常改变的数据
          数据的正确与否对最终结果影响很大的。
          例如:商品的库存,银行的汇率,股市的牌价。
      
    • Mybatis中的一级缓存和二级缓存

      一级缓存:
          * 它指的是Mybatis中SqlSession对象的缓存。
          * 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用。
          * 当SqlSession对象消失时,mybatis的一级缓存也就消失了。
          * 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
      
      二级缓存:
          * 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
          * 二级缓存的使用步骤:
              * 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
              * 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
              * 第三步:让当前的操作支持二级缓存(在select标签中配置)
      

      cash1.JPG
      cash2.JPG
      cash3.JPG

Mybatis中的注解开发

这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper(即UserDap.xml、AccountDao.xml等配置文件)映射文件了。在Mybatis中只能使用映射配置文件和注解配置一种方式,所以使用注解配置时,应将映射配置文件移除。

1. 单表的CUDA操作

首先针对单表的CUDA操作,只需要在UserDao接口文件的相关方法上添加配置即可。Mybatis中有四种SQL相关的语句配置:

  • @Insert: 实现新增
  • @Update: 实现更新
  • @Delete: 实现删除
  • @Select: 实现查询

以User类的接口UserDao为例,程序改写如下:

public interface UserDao {
    /*
    查询所有用户,同时获取用户下所有的账户信息
   */
    @Select("select * from user")
    @Results(id = "userMap", value={
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "username", property = "username"),
            @Result(column = "address", property = "address"),
            @Result(column = "sex", property = "sex"),
            @Result(column = "birthday", property = "birthday"),
    })
    List<User> findAll();

    /**
     * 按ID查询用户信息
     * @param userID
     */
    @Select("select * from user where id = #{uid}")
    @ResultMap(value = {"userMap"})
    User findById(Integer userID);

    /**
     * 插入保存user
     * @param u
     */
    @Insert("insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
    @ResultMap(value = {"userMap"})
    void saveUser(User u);

    /**
     * 更新信息
     * @param u
     */
    @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}")
    @ResultMap(value = {"userMap"})
    void updateUser(User u);

    /**
     * 删除用户
     * @param userID
     */
    @Delete("delete from user where id = #{id}")
    @ResultMap(value = {"userMap"})
    void deleteUser(Integer userID);

    /**
     * 按名称模糊查询用户信息
     */
    @Select("select * from user where username like #{username}" )
    @ResultMap(value = {"userMap"})
    //@Select("select * from user where username like '%${value}%'" )
    List<User> findByName(String username);

    /**
     * 聚合函数:查询总记录条数
     */
    @Select("select count(id) from user")
    @ResultMap(value = {"userMap"})
    int findTotal();
}
  • 只需要在方法之上添加 @Select(“SQL语句”),@Insert(“SQL语句”),@Update(“SQL语句”),@Delect(“SQL语句”)。
  • 另外,若User类中的属性与数据库中user表的列名不一致,需要增加 @Results() 注解配置,类似于UserDao.xml中的配置。
    userMap.jpg

2. 多表操作配置

  • 一对一配置
    onetoone.jpg

  • 一对多配置
    many.jpg