Java框架 - Mybatis

1. Mybatis概述

  mybatis是一个持久层框架,用java编写的。

  它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程。

  它使用了ORM思想实现了结果集的封装。

  ORM:

    Object Relational Mapping 对象关系映射

    简单的说:

      就是把数据库表和实体类及实体类的属性对应起来

      让我们可以操作实体类就实现操作数据库表。

      user         User

      user_id    userId

      user_name   userName

    今天我们需要做到  

      实体类中的属性和数据库表的字段名称保持一致。

      user         user

      user_id    user_id

      user_name   user_name

2. Mybatis的环境搭建及入门案例

  (1)创建maven工程并导入坐标(在pom.xml中)

  (2)创建实体类和dao的接口

  (3)创建Mybatis的主配置文件

    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">
<!-- 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/eesy_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="woaini1314"/>
            </dataSource>
        </environment>
    </environments>

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

 

  (4)创建映射配置文件

    IUserdao.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.itheima.dao.IUserDao"> <!-- namespace是dao包的全限定类名-->
    <!--配置查询所有-->
    <select id="findAll" resultType="com.itheima.domain.User"> <!-- id 是dao中的方法名称 resultType是返回值类型 既将返回值封装成User并且放到list集合中-->
        select * from user
    </select>
</mapper>

 

  * 环境搭建的注意事项:

    第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保存一致

      在mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper

      所以:IUserDao 和 IUserMapper是一样的   以后可能会遇到别人叫做mapper

     第二个:在idea中创建目录的时候,它和包是不一样的

      包在创建时:com.itheima.dao是三级结构

      目录在创建时:com.itheima.dao是一级目录

    第三个:mybatis的映射配置文件必须和dao接口的包结构相同

    第四个:映射配置文件的mapper标签namesapce属性的取值必须是dao接口的全限定类名

    第五个:映射配置文件的操作配置(select,insert等),id属性的取值必须是dao接口的方法名

    

    当我们遵从了第三、四、五点之后,我们在开发中就无须再写dao的实现

         第一步:读取配置文件

      第二步:创建SqlSessionFactory工厂

      第三步:创建SqlSession

      第四步:创建Dao接口的代理对象

      第五步:执行dao中的方法

      第六步:释放资源

      注意事项:

        不要忘记在映射配置中告知mybatis要封装到那个实体类

        配置的方式:指定实体类的全限定类名

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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 java.io.InputStream;
import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 * mybatis的入门案例
 */
public class MybatisTest {

    /**
     * 入门案例
     * @param args
     */
    public static void main(String[] args)throws Exception {
        // 1. 读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in); // 工厂会封装 解析配置文件

        // 3. 使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();

        // 4. 使用SqlSession创建Dao接口的代理对象
        IUserDao userDao = session.getMapper(IUserDao.class);

        // 5. 使用代理对象执行方法
        List<User> users = userDao.findAll();
        for (User user:users){
            System.out.println(user);
        }

        // 6. 释放资源
        session.close();
        in.close();
    }
}

 

  mybatis基于注解的入门案例:

    把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句

    同时需要在SqlMaoConfig.xml中的mapper配置时使用class属性指定dao接口的全限定类名

<?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/eesy_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="woaini1314"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件
        如果是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名
    -->
    <!--<mappers>-->
        <!--<mapper resource="com/itheima/dao/IUserDao.xml"/>-->
    <!--</mappers>-->
    <mappers>
        <mapper class="com.itheima.dao.IUserDao"/>
    </mappers>
</configuration>
SqlMapConfig.xml

 

package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 *
 * 用户的持久层接口
 */
public interface IUserDao {

    /**
     * 查询所有操作
     * @return
     */
    @Select("select * from user")
    List<User> findAll();
}
dao接口

 

    明确:

      我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。

      不管使用XML还是注解配置。

      但是Mybatis是支持写dao实现类的。

入门案例中mybatis的设计模式分析:

 

(3)自定义Mybatis的分析

  mybatis在使用代理dao的方式实现增删改查时做了些什么事呢?

    只有两件事:

      第一:创建代理对象

      第二:在代理对象中调用selectList

  自定义Mybatis能通过入门案例看到类:

    class Resources

    class SqlSessionFactoryBuilder

    interface SqlSessionFactory

    interface SqlSession 

 

3.基础CRUD操作

  第一步,在IUserDao接口中写入方法

  第二步,在IUserDao.xml中配置方法的参数、返回值类型以及sql语句

  第三步,执行测试方法

package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 *
 * 用户的持久层接口
 */
public interface IUserDao {

    /**
     * 查询所有操作
     * @return
     */
//    @Select("select * from user")
    List<User> findAll();
    void saveUser(User user);
    void updateUser(User user);
    void deleteUser(Integer id);

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

    /**
     * 模糊查询
     */
    List<User> findByName(String username);

    /**
     * 查询总用户数
     * @return
     */
    int findTotal();
}
IUserDao

 

<?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.itheima.dao.IUserDao">
    <!--配置查询所有-->
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from user
    </select>
    <!-- 保存用户-->
    <!-- parameterType 表示方法的参数类型-->
    <insert id="saveUser" parameterType="com.itheima.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="com.itheima.domain.User">
      update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id = #{id};
    </update>

    <!-- 删除用户 占位符可以随便写,因为只有一个参数-->
    <delete id="deleteUser" parameterType="Integer">
      delete from user where id = #{1};
    </delete>

    <!-- 根据id 查询用户-->
    <select id="findById" parameterType="Integer" resultType="com.itheima.domain.User">
      select * from user where id = #{id};
    </select>

    <!-- 模糊查询 -->
    <select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
      select * from user where username like #{name};
    </select>

    <!-- 获取用户的总记录数-->
    <select id="findTotal" resultType="integer">
      select count(id) from user;
    </select>
</mapper>
IUserDao.xml

 

package com.itheima.test;

import com.itheima.dao.IUserDao;
import com.itheima.domain.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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 * mybatis的入门案例
 */
public class MybatisTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before // 用于在测试方法之前执行
    public void init() throws Exception {
        // 1. 读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");

        // 2. 创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in); // 工厂会封装 解析配置文件

        // 3. 使用工厂生产SqlSession对象
        sqlSession = factory.openSession();

        // 4. 使用SqlSession创建Dao接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.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 user : users) {
            System.out.println(user);
        }
    }

    /**
     * 测试保存操作
     */
    @Test
    public void testSave() throws Exception {
        User user = new User();
        user.setUsername("chris");
        user.setAddress("星辰园");
        user.setSex("男");
        user.setBirthday(new Date());
        System.out.println("保存操作之前"+user);
        //5. 执行保存方法
        userDao.saveUser(user);
        System.out.println("保存操作之后"+user);


    }

    @Test
    public void updateUser() throws Exception {
        User user = new User();
        user.setId(49);
        user.setUsername("mybatis updateUser02");
        user.setAddress("上海市松江区");
        user.setSex("女");
        user.setBirthday(new Date());
        //5. 执行更新方法
        userDao.updateUser(user);
    }

    @Test
    public void testDelete() throws Exception {
        userDao.deleteUser(48);
    }

    @Test
    public void testFindById() throws Exception {
        User user = userDao.findById(46);
        System.out.println(user);
    }

    /**
     * 测试模糊查询
     * @throws Exception
     */
    @Test
    public void testFindByName() throws Exception {
        List<User> users = userDao.findByName("%王");
        for(User user:users){
            System.out.println(user);
        }

    }

    /**
     * 测试查询总记录条数
     * @throws Exception
     */
    @Test
    public void testFindTotal() throws Exception {
        int total = userDao.findTotal();
        System.out.println(total);
    }


}
测试类

 

4. OGNL表达式

  Object Graphic Navigation Language

   对象    图         导航    语言

  * 它是通过对象的取值方法来获取数据。在方法上把get给省略了。

  比如:我们获取用户名称

    类中的写法:user.getUsername();

    OGNL表达式写法:user.username

  mybatis中为什么能直接写username,而不用user.呢?

    因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名

对象中包含对象来进行查询:(以后用于多对象的条件查询)

IUserDao中:

List<User> findUserByVo(QueryVo vo);

 

IUserDao.xml中:

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

 

测试类中:

@Test
    public void testFindByVo() throws Exception {
        QueryVo vo = new QueryVo();
        User user = new User();
        user.setUsername("%王%");
        vo.setUser(user);
        List<User> users = userDao.findUserByVo(vo);
        for(User u:users){
            System.out.println(u);
        }

    }

 

5. 如果实体类的成员属性名与数据库的列名不一致怎么办? 取别名或使用resultMap

  当查询时,如果查询到的数据因为无法与User实体类的属性对应,解决方案方法:

  第一种:可以采用取别名的方法:(执行效率高,一次解析)

<!--配置查询所有-->
    <select id="findAll" resultType="com.itheima.domain.User">
        select id as userId,username as userName from user
    </select>

 

  第二种:配置查询结果的列名和实体类的属性名的对应关系:(开发效率高,两次解析)使用后,需要在下面的CRUD标签里将resultType换为resultMap,且与唯一标识一致

<!--配置 查询结果的列名和实体类的属性名的对应关系-->
    <!-- id表示自己给取的唯一标识 type表示结果封装的实体类-->
    <resultMap id="userMap" type="com.itheima.domain.User">
        <!--主键字段的对应 property表示java中的属性字段 column表示数据库中的列名-->
        <id property="userid" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
    </resultMap>

 

 6.properties、typeAliases、package标签

properties:可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息

   resource属性: 常用的用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。

   url属性:是要求按照Url的写法来写地址

   URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。

   它的写法:

    http://localhost:8080/mybatisserver/demo1Servlet

    协议      主机     端口       URI

   URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。

<!-- 配置环境 -->
    <properties resource="jdbcConfig.properties"></properties>
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>

 

 

(2)typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就不再区分大小写

    <typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>

   用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写

 

 

 在mappers标签中:package的作用

 

 7. Mybatis连接池

  mybatis连接池提供了3种方式的配置:

    配置的位置:

      (1)主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式

        type属性的取值:

          POOLED:采用传统的javaxsql.DataSource规范中的连接池,mybatis中有针对规范的实现

          UNPOOLED:采用传统的获取连接的方式,虽然也实现了javaxsql.DataSource接口,但是并没有使用池的思想

          JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到的DataSource是不一样的

             注意:如果不是web或者maven的war工程,是不能使用的。

             我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池

8. Mybatis中的事务

  什么是事务?

  事务的四大特性ACID

  不考虑隔离性会产生的3个问题

  解决办法:四种隔离级别

 

  mybatis是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚,opensession方法传入参数true就可设置为自动提交事务

 

9.Mybatis映射文件的SQL深入

  *动态SQL:if、where、foreach标签

  if标签的使用:(其中test测试的是传入参数中username是否为空、#{}中的值也是javabean的变量,其他的都是数据库变量)

 

<!--根据条件查询 -->
    <select id="findUserByCondition" parameterType="com.itheima.domain.User" resultMap="userMap">
        select  * from user where 1 = 1
        <if test="username!=null">
            and username = #{username}
        </if>
        <if test="id != null">
            and id = #{id}
        </if>
    </select>

 

 

  where标签的使用:(能让我们省略永远1=1的条件,帮我们自动拼接sql)

    <select id="findUserByCondition" parameterType="com.itheima.domain.User" resultMap="userMap">
        select  * from user
        <where>
        <if test="username!=null">
            and username = #{username}
        </if>
        <if test="id != null">
            and id = #{id}
        </if>
        </where>
    </select>

 

 

  foreach标签:(用于条件的子查询)

<!--根据queryvo中的id集合实现查询用户列表-->
    <select id="findUserInIds" parameterType="com.itheima.domain.QueryVo" resultMap="userMap">
      select * from user
      <where>
          <if test="ids != null and ids.size() > 0 ">
            <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
              #{uid}
            </foreach>
          </if>
      </where>
    </select>

 

 

  sql标签:(用于重复的sql语句)

<!--了解的内容:抽取重复的sql语句-->
    <sql id="defaultUser">
        select * from user
    </sql>
    
    <!--配置查询所有-->
    <select id="findAll" resultType="com.itheima.domain.User">
      <include refid="defaultUser"></include>
    </select>

 

10.Mybatis中的多表查询

  

 

   完成account一对一操作:(建立实体类关系的方式)

Account类:

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private double money;

    //从表实体应该包含一个主表实体的对象引用
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}
Account

 

IAccountDao.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.itheima.dao.IAccountDao">

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>
    </resultMap>
    <!--配置查询所有-->
    <select id="findAll" resultMap="accountUserMap">
        select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
    </select>

    <!--查询所有账户同时包含用户名和地址信息-->
    <select id="findAllAccount" resultType="accountuser">
        select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
    </select>

</mapper>

 

mybatis实现一对多查询:如查询所有用户信息,同时获取该用户下所有的账户信息

  映射配置及sql语句:

<!--配置 查询结果的列名和实体类的属性名的对应关系-->
    <!-- id表示自己给取的唯一标识 type表示结果封装的实体类-->
    <resultMap id="userMap" type="com.itheima.domain.User">
        <!--主键字段的对应 property表示java中的属性字段 column表示数据库中的列名-->
        <id property="id" column="id"></id>
        <!--非主键字段的对应-->
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!-- 配置user对象中accounts集合的映射 ofType表示集合中元素的类型 -->
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>
<!--一对多查询的使用-->
    <select id="findMany" resultMap="userMap">
      select * from user u left outer join account a on u.id = a.uid
    </select>

 

  实体类User:

package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 */
public class User implements Serializable{

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    //一对多关系映射:主表实体应该包含从表实体的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
View Code

 

  测试类方法:

@Test
    public void testFindMany() throws Exception {
        List<User> users = userDao.findMany();
        for(User user : users){
            System.out.println("----每个用户的信息---");
            System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }

 

  mybatis实现多对多查询:

 

 

 

 

   实体类:

package com.itheima.domain;

import java.io.Serializable;
import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 */
public class Role implements Serializable {

    private Integer roleId;
    private String roleName;
    private String roleDesc;

    //多对多的关系映射:一个角色可以赋予多个用户
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}
Role实体类

 

  IRoleDao.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.itheima.dao.IRoleDao">

    <!--定义role表的ResultMap-->
    <resultMap id="roleMap" type="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="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>

 

 从用户表查到角色的sql语句:  

    <!--查询所有-->
    <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>

 

11. Mybatis中的延迟加载

  问题:在一对多中,当我们有一个用户,他有100个账户

    那么在查询用户的时候要不要把关联的账户查出来?

    在查询账户时,要不要把关联的用户查出来呢?

  * 在查询用户时,用户下的账户信息,应该是什么时候使用就什么时候查询的。

  * 在查询账户时,账户的所属用户信息应该是随着用户查询时一起查询出来

  (1)什么是延迟加载?

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

  (2)什么是立即加载?

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

  在对应的四种表关系中:一对多,多对一,一对一,多对多

    一对多,多对多:通常情况下我们都是采用延迟加载

    多对一,一对一:通常情况下我们都是采用立即加载

12.Mybatis中的缓存

  什么是缓存?

    存在于内存中的临时数据

  为什么使用缓存?

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

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

    适用于缓存:

      经常查询并且不经常改变。

      数据的正确与否对最终结果影响不大的。

    不适用于缓存:

      经常改变的数据

      数据的正确与否对最终结果影响很大的。例如:商品的库存、银行的汇率、股市的牌价

  (1)Mybatis中的一级缓存

    一级缓存:它指的是mybatis中SqlSession对象的缓存

      当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中

      该区域的结构是一个Map,当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。

      当SqlSession对象消失时,mybatis的一级缓存就消失了。也可通过sqlsession的clearCache()方法清除缓存

      * 注意:当调用Sqlsession的修改,添加,删除,commit(),close()以及clearCache()等方法时,mybatis就会清空一级缓存

  (2)Mybatis的二级缓存:其存放的内容是数据,而不是对象,因此内存地址会不同

    二级缓存:它指得是Mybatis中SqlSessionFactory对象缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

    二级缓存的使用步骤:

         第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

 

      第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

      第三步:让当前的操作支持二级缓存(在select标签中配置)

<!-- 开启user支持二级缓存 -->
    <cache/>

    <!-- 根据id查询用户 -->
    <select id="findById" parameterType="INT" resultType="user" useCache="true">
        select * from user where id = #{uid}
    </select>

 

13.Mybatis的注解开发

  注意事项:如果采用注解开发的话,dao下不能有dao.xml的配置文件,否则mybatis会报错    

package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

/**
 * @author 黑马程序员
 * @Company 
 * 在mybatis中针对,CRUD一共有四个注解
 *  @Select @Insert @Update @Delete
 */
public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    List<User> findAll();

    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);

    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
    void updateUser(User user);

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

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user  where id=#{id} ")
    User findById(Integer userId);

    /**
     * 根据用户名称模糊查询
     * @param username
     * @return
     */
//    @Select("select * from user where username like #{username} ")
    @Select("select * from user where username like '%${value}%' ")
    List<User> findUserByName(String username);

    /**
     * 查询总用户数量
     * @return
     */
    @Select("select count(*) from user ")
    int findTotalUser();
}

 

 

  mybatis注解建立实体类属性与数据库表中列的对应关系如何实现?- - - - 通过Results 和 Result注解 如果其他注解要引用,可通过ResultMap注解引用

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
    List<User> findAll();

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user  where id=#{id} ")
    @ResultMap("userMap")
    User findById(Integer userId);

 

  mybatis注解开发一对一的查询配置:one=@One表示一对一的查询配置,select属性为查询到用户表的方法,fetchType为加载时机类型

 /**
     * 查询所有账户,并且获取每个账户所属的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id="accountMap",value = {
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();

 

  

  mybatis注解开发一对多的查询配置:many=@Many,提供一个查询账户的方法

/**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts",column = "id",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                                fetchType = FetchType.LAZY))
    })
    List<User> findAll();

 

  mybatis注解配置二级缓存:为dao接口配置给注解,并在SqlMapConfig中配置二级缓存即可

@CacheNamespace(blocking = true)

 

posted @ 2020-11-14 13:26  五号世界  阅读(258)  评论(0编辑  收藏  举报