Mybatis


1.Mybatis

1.1 Mybatis的概述

Mybatis是一款优秀的持久层Java框架。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动、创建连接创建 statement 等繁杂过程。

mybatis 通过xml注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 Mybatis 框架执行 sql 并将结果映射为 java 对象并返回。

它采用ORM思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

  • ORM (Object Relational Mappging) 对象关系映射

    就是把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表。

1.2 Mybatis的入门

XML文件方式

  1. 创建maven工程

    • 配置pom文件定位

      <groupId>xyz.sky03</groupId>
      <artifactId>Mybatis</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>jar</packaging>
      
      <dependencies>
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.4.5</version>
          </dependency>
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>5.1.47</version>
          </dependency>
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.12</version>
          </dependency>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.10</version>
          </dependency>
      </dependencies>
  2. 添加配置文件

    • 添加log4j配置文件(用于打印日志)

      # Set root category priority to INFO and its only appender to CONSOLE.
      #log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
      log4j.rootCategory=debug, CONSOLE, LOGFILE
      
      # Set the enterprise logger category to FATAL and its only appender to CONSOLE.
      log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
      
      # CONSOLE is set to be a ConsoleAppender using a PatternLayout.
      log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
      log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
      log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
      
      # LOGFILE is set to be a File appender using a PatternLayout.
      log4j.appender.LOGFILE=org.apache.log4j.FileAppender
      #此项是配置日志输出路径,如果路径错误会自动输出到项目根目录里
      log4j.appender.LOGFILE.File=User/sky03/axis.log
      log4j.appender.LOGFILE.Append=true
      log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
      log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
    • 添加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/mybatis_practice"/>
                      <property name="username" value="root"/>
                      <property name="password" value="admin"/>
      
                  </dataSource>
              </environment>
          </environments>
      
          <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
          <mappers>
              <mapper resource="com/itheima/dao/IUserDao.xml"/>
          </mappers>
      </configuration>
  3. 创建domain类 User,记得实现Serializable接口,实现序列化,并在数据库中创建与之对应的表

  4. 创建UserDao接口,定义方法

    package xyz.sky03.dao;
    
    import xyz.sky03.domain.User;
    import java.util.List;
    
    public interface UserDao {
        List<User> findAll();
    }
  5. 创建映射配置文件

    • 因为是maven项目,所以要在resources文件夹中创建与UserDao接口的包名相同的文件夹路径,即:创建xyz/sky03/dao文件夹。这样做的目的是不用写UserDao的实现类

    • 在该文件夹创建UserDaoMapper.xml,文件名随意,一般是接口名+Mapper

      <?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="xyz.sky03.dao.UserDao">
          <!--配置查询所有,resultType的作用是指定将查询到的结果封装给哪个javabean类-->
          <select id="findAll" resultType="xyz.sky03.domain.User">
              <!-- 将要执行的sql语句写在下面 -->
              select * from user
          </select>
      </mapper>
  6. 配置完成,执行测试,在test文件夹中创建测试类UserDaoTest

    import xyz.sky03.dao.UserDao;
    import xyz.sky03.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;
    
    public class MybatisTest {
    
        /**
         * 入门案例
         */
        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接口的代理对象
            UserDao userDao = session.getMapper(UserDao.class);
            //5.使用代理对象执行方法
            List<User> users = userDao.findAll();
            for(User user : users){
                System.out.println(user);
            }
            //6.释放资源
            session.close();
            in.close();
        }
    }
  7. 项目示例:Mybatis入门基于XML

注解方式

  1. 删掉UserDaoMapper.xml文件,直接在UserDao接口的方法上添加注解

    package xyz.sky03.dao;
    
    import xyz.sky03.domain.User;
    import org.apache.ibatis.annotations.Select;
    import java.util.List;
    
    public interface UserDao {
        @Select("select * from user")
        List<User> findAll();
    }
  2. SqlMapConfig.xml主配置文件中改变映射,在<mappers> 标签中的<mapper>的属性改为class,值为UserDao的全类名

    <mappers>
        <mapper class="xyz.sky03.dao.UserDao"/>
    </mappers>
  3. 然后就没了,好像不用指定返回结果封装在哪个实体类上

2.自定义Mybatis框架

自定义Mybatis框架的目的是熟悉Mybatis框架的原理以及执行流程。

3.Mybatis的CRUD

示例项目:Mybatis的CRUD

3.1 查询操作

查询操作可以参考以上 Mybatis的入门

3.2 保存操作

保存必然是用insert语句

在XML配置方式的基础上再进行一下操作:

  1. 在UserDao接口中添加保存用户的方法

    /**
     * 保存用户
     * @param user
     */
    void saveUser(User user);
  2. 在映射文件UserDao.xml文件中添加插入语句

    • 标签:insert语句自然要用 <insert> 标签

    • 属性:id 指定UserDao中保存用户的方法;parameterType 指定UserDao中saveUser方法的参数类型

    • 因为user表的id是自增长的,所以使用 insert into table() values()] 的形式

    • values里面的值,使用Mybatis的表达式

      • #{属性},属性是对应字段的 parameterType 指定的类的属性

      • #{username},就是自动获取User类中的username属性

    <!-- 保存用户 -->
    <insert id="saveUser" parameterType="xyz.sky03.domain.User">
        insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday});
    </insert>
  3. 配置测试方法,并执行

3.3 更新操作

  1. 在UserDao接口中定义更新方法

    void updateUser(User user);
  2. 在映射文件UserDao.xml中添加更新的SQL语句:

    <update id="updateUser" parameterType="xyz.sky03.domain.User">
        update user set username=#{username},sex=#{sex},address=#{address},birthday=#{birthday} where id=#{id}
    </update>
  3. 配置测试方法,并执行

3.4 删除操作

  1. 在UserDao接口中定义删除方法

    void deleteUser(Integer userId);
  2. 在映射文件UserDao.xml中添加删除的SQL语句:

    <delete id="deleteUser" parameterType="int">
        <!--uid只是一个占位符,因为属性只有一个,所以站位符可以随便写-->
        delete from user where id = #{uid}
    </delete>
    • 其中parameterType的值可以这么写:
      • int
      • INT
      • Integer
      • java.lang.Integer
    • 之所能这么写,是因为Mybatis给int配置了别名,具体可 参考
  3. 配置测试方法,并执行

3.5 其他查询

3.5.1 根据id查询

  1. 在UserDao接口中定义查询的方法

    //通过id查用户
    User findById(Integer userId);
  2. 在映射文件UserDao.xml中添加SQL语句:

    parameterType用来指定dao中查询方法的参数类型,resultType指定将结果封装给哪个类

    <select id="findById" parameterType="int" resultType="xyz.sky03.domain.User">
        select * from user where id = #{uid}
    </select>
  3. 配置测试类,并执行。

3.5.2 模糊查询

  • 方法一

    1. 在UserDao接口中定义查询的方法

      //通过name模糊查询
      List<User> findByName(String username);
    2. 在映射文件UserDao.xml中添加SQL语句:

      • 方式一

        这种方式使用的是SQL预编译,所以不会被SQL注入。

        <select id="findByName" parameterType="string" resultType="xyz.sky03.domain.User">
            select * from user where username like #{name}
        </select>
      • 方式二 (不常用,了解)

        like后面直接使用value,value是Mybatis的源码里面的key;

        这种是使用字符串拼接的SQL语句,容易被SQL注入。

         <select id="findByName" parameterType="string" resultType="xyz.sky03.domain.User">
            select * from user where username like '%${value}%'
        </select>
    3. 配置测试方法

      使用方式一的Sql语句由于不能写 %,所以测试方法要在参数的两头多加个 %,方式二则不用,Like this:

      @Test
      public void testFindByName(){
          List<User> users = userDao.findByName("%王%");
          for(User user : users){
              System.out.println(user);
          }
      }

3.5.3 查询总记录数

  1. 在UserDao接口中定义查询的方法

    //查询总用户数
    int findTotal();
  2. 在映射文件UserDao.xml中添加SQL语句:

    <!--查询总记录数-->
    <select id="findTotal" resultType="int" >
        select count(id) from user; 
    </select>
  3. 编写测试方法,并执行

3.5.4 获取新增用户的id

  • 可以在insert语句后加一条 (必须是在insert语句后)

    select last_insert_id();
  • 在Mybatis中:就是在XML文件中编写

    • 标签:<selectKey>
    • 位置:<insert>标签内
    • 属性:
      • KeyProperty:表示封装实体类User的id;
      • KeyColumn:表示数据库中的字段id;
      • resultType:表示查询到的结果封装成什么类型(别忘了配置这个属性);
      • order:有两个值:AFTER表示后执行,BEFORE表示先执行;
    <!-- 保存用户 -->
    <insert id="saveUser" parameterType="xyz.sky03.domain.User">
        <selectKey KeyProperty="id" KeyColumn="id" order="AFTER" resultType="int">
            select last_insert_id();
        </selectKey>
        insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday});
    </insert>
  • 配置测试类,执行保存之后会自动把id封装到user对象里

    @Test
    public void testSaveUser(){
        User user=new User();
        user.setUsername("亚索");
        user.setSex("男");
        user.setAddress("艾欧尼亚");
        user.setBirthday(new Date());
        System.out.println("保存之前:"+user);//输出的id为null
        userDao.saveUser(user);
        System.out.println("保存之后:"+user);//输出的id为数据库里的id
    }

4.Mybatis参数的深入

4.1 OGNL表达式

概念

OGNL全称:Object Graphic Navigation Language(对象 图 导航 语言)

  • Apache开发出来的
  • Struts2也有使用

写法

通过对象的取值方法来获取数据。在写法上把get给省略了。
例如:
获取用户名称
正常写法:user.getUsername()
OGNL表达式:user.username

  • Mybatis中的SQL语句为什么能直接写username,二不用user.呢?

    因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名。而如果没有指定parameterType属性,那就只能写完整的OGNL表达式:user.username

5.2 pojo包装对象作为查询条件

pojo对象:简单的Javabean对象

开发中查询条件有时候是复杂的综合查询条件,不仅包括用户查询条件还包括其他的查询条件(比如用户购买的商品信息也作为查询条件),通过把很多条件包装成一个pojo对象,然后再传递查询条件

例:还是以模糊查询为例

  1. 首先在domain包中创建QueryVo类,把User类作为其属性,并设置getter和setter方法

  2. 再在UserDao接口中定义查询方法

    /**
     * 根据queryVo中的条件查询用户
     */
    List<User> findByVo(QueryVo vo);
  3. 在映射文件UserMapper.xml中定义SQL语句。parameterType已经指定了QueryVo,而username是User的属性,User又是QueryVo的属性,所以可以直接写user.username

    <!--根据queryVo中的条件查询用户-->
    <select id="findByVo" parameterType="xyz.sky03.domain.QueryVo" resultType="xyz.sky03.domain.User">
        select * from user where username like #{user.username};
    </select>
  4. 编写测试方法并执行

    /**
     * 测试QueryVo作为查询条件
     */
    @Test
    public void testFindByVo(){
        QueryVo vo = new QueryVo();
        User user = new User();
        vo.setUser(user);
        user.setUsername("%亚%");//这里为了模糊查询,要加%%
        List<User> users = userDao.findByVo(vo);
        for(User u : users){
            System.out.println(u);
        }
    }

5.3 Mybatis中数据库字段与实体类的属性不对应

Windows系统的MySQL数据库是不区分大小写的(字段等)
而Linux系统的MySQL数据库是严格区分大小写的

在Mybatis中数据库字段与实体类的属性名称如果不一致,会报错,会产生一系列的问题。比如:查询语句查询到的结果封装不到实体类上等

  • 假设:实体类User中的属性为userId、userName、userSex、userBirthday、userAddress

    数据库中的字段依然为:id、username、sex、birthday、address

解决方案:

  • 方案一:SQL语句层面上解决

    • 思路:起别名

    • 性能:相比方案二较快

    • 缺点:需要在每个地方都这么写,麻烦

    • 例:直接把SQL语句用别名的方式

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userMap">
        select id as userId,username as userName,address as userAddress,sex as userSex,birthday as userBirthday from user;
    </select>
  • 方案二

    • 思路:映射文件中配置查询结果的字段名和实体类的属性名的对应关系

    • 性能:相比方案一较慢,因为多了一步,二次解析xml文件

    • 配置<resultMap> 标签

      • 属性 id :唯一标识符;属性 type :指定实体类的全类名

      • 子标签 <id> :指定主键字段的对应关系

      • 子标签 <result> :指定非主键字段的对应关系

      • <id><result> 的共同属性:

        • property :指定实体类中的属性

        • column :指定数据库中的字段

      <!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
      <resultMap id="userMap" type="uSeR">
          <!-- 主键字段的对应 -->
          <id property="userId" column="id"></id>
          <!--非主键字段的对应-->
          <result property="userName" column="username"></result>
          <result property="userAddress" column="address"></result>
          <result property="userSex" column="sex"></result>
          <result property="userBirthday" column="birthday"></result>
      </resultMap>
    • 配置完resultMap还不行,还要在执行SQL的地方指定

      • <select> 的resultType属性改为resultMap,并指定已经配置好的<resultMap> 的id,所以它会再次解析xml寻找id对应的resultMap,造成性能浪费
      <mapper namespace="xyz.sky03.dao.IUserDao">
          <!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
          <resultMap id="userMap" type="xyz.sky03.domain.User">
              <!-- 主键字段的对应 -->
              <id property="userId" column="id"></id>
              <!--非主键字段的对应-->
              <result property="userName" column="username"></result>
              <result property="userAddress" column="address"></result>
              <result property="userSex" column="sex"></result>
              <result property="userBirthday" column="birthday"></result>
          </resultMap>
      
          <!-- 查询所有 -->
          <select id="findAll" resultMap="userMap">
              select * from user;
          </select>
      </mapper>
    • 优点:虽然性能有所降低,但是带来的好处却是很方便使用。在每个需要的地方加上resultMap属性并指定id即可

6.properties标签使用

  • 标签 <properties><mapper> 属性二者都适用

  • 位置:在 <configuration> 内部

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

  • 属性 resource (常用):用于指定配置文件位置

  • 属性 url :用URL的写法来定位配置文件位置(网址)

  • 例:

    • 标签内部定义连接信息(这样没什么意义)

      <configuration>
      <properties>
          <property name="driver" value="com.mysql.jdbc.Driver"></property>
          <property name="url" value="jdbc:mysql:///mybatis_practice"></property>
          <property name="username" value="root"></property>
          <property name="password" value="admin"></property>
      </properties>
      
      <!-- ----需要在dataSource标签中指定---- -->
      
      <environments default="mysql">
          <!-- 配置mysql的环境-->
          <environment id="mysql">
              <!-- 配置事务 -->
              <transactionManager type="JDBC"></transactionManager>
              <!--配置连接池-->
              <dataSource type="POOLED">
                  <property name="driver" value="${driver}"></property>
                  <property name="url" value="${url}"></property>
                  <property name="username" value="${username}"></property>
                  <property name="password" value="${password}"></property>
              </dataSource>
          </environment>
      </environments>
      </configuration>
    • 标签外部引入配置文件,这样就能把数据库连接信息写在配置文件里 (注意引用的时候带上 jdbc. )

      <configuration>
      <properties resource="jdbcConfig.properties"></properties>
      
      <environments default="mysql">
          <environment id="mysql">
              <transactionManager type="JDBC"></transactionManager>
              <dataSource type="POOLED">
                  <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>
      </environments>
      </configuration>
    • jdbcConfig.properties文件内容如下:

      jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/mybatis_practice
      jdbc.username=root
      jdbc.password=admin

7.typeAliases标签

位置:主配置文件中的 <configuration> 标签内
子标签:<typeAlias><package>

7.1 typeAlias标签

  • 用于配置别名
  • 位置:<typeAliases> 标签内
  • 属性 type :用于指定实体类的全类名
  • 属性 alias :指定别名,别名在用的时候可以不区分大小写,用parameterType 来指定别名
  • <typeAliases> 只能给实体类配置别名

7.2 package标签

  • 作用:用于指定要配置别名的包,当指定后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写
  • 位置:<typeAliases> 标签内
  • 属性 name:指定包的全路径
  • 注解和xml方式都可以用

7.3 mappers标签中的package标签

  • 用于指定 Dao接口 所在的包,指定了之后就不用再写mapper标签了
  • 作用和mapper标签一样
  • 属性 name :指定 Dao接口 所在的包

8.Mybatis的连接池

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

配置的位置:

  • 在主配置文件SqlMapConfig.xml中的 <dataSource>标签,其type 属性就是指定哪种连接池方式

  • type 属性取值:

    • POOLED:采用传统的javax.sql.DataSource规范中的连接池,Mybatis对该规范有实现

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

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

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

      • 比如:Tomcat服务器采用的连接池是dbcp连接池。

9.Mybatis的事务控制

  • Mybaits默认是关闭事务的手动提交

  • 开启事务自动提交

    • 在创建SqlSession对象的时候,给openSession()加个参数true,表示自动提交,也就是openSession(true)

10.动态SQL

10.1 if标签

有时候查询是需要某个条件成立的,这时候就需要在<select> 标签中使用 <if> 标签

  • 属性 test:判断条件
    • 如果为true,就在整个SQL语句后加上<if>标签里的内容
    • 如果为false,就省略<if> 标签里的内容
    • 所以为了整个SQL语句完整,就在where后面加了个 1=1
    • 如果需要两个条件同时成立才可以,可以在两个条件中间加 and ,而不是 &&
<!--通过条件查询-->
<select id="findByCondition" resultType="xyz.sky03.domain.User" parameterType="user">
    select * from user where 1=1
    <if test="username != null">
        and username = #{username}
    </if>
</select>

10.2 where标签

顾名思义:<where> 标签和SQL语句中where作用是一样的

<select id="findByCondition" resultType="xyz.sky03.domain.User" parameterType="user">
    select * from user
    <where>
        <if test="username != null">
            and username = #{username}
        </if>
    </where>
</select>

10.3 foreach标签

使用场景:有个集合需要遍历,并作为查询条件

标签:<foreach>
属性:

  • collection :指定要遍历的集合,该集合得是queryvo实体类中的属性
  • open :指定的字符串是放在 遍历出来的值得前面
  • close :指定的字符串放在 遍历出来的值得后面
  • item :项目,指定的值是什么 <foreach> 标签内的#{}里面就写什么
  • separator :指定遍历出来的结果以什么符号间隔

遍历出来的结果大概就是:

and id in 15,25,35 #{id}被换成了遍历出来的id

<select id="findByCondition" resultType="xyz.sky03.domain.User" parameterType="queryvo">
    select * from user
    <where>
        <if test="ids != null and ids.size()>0">
            <foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
</select>

10.4 sql标签

用途:抽取重复的SQL语句

例:

<sql id="defaultUser">
    select * from user
</sql>

在其他地方引用:

<select id="findAll" resultType="xyz.sky03.domain.User">
    <include refid="defaultUser"></include> 
</select>

11.Mybatis的多表操作

11.1 表之间的关系

4种:一对多、多对一、一对一、多对多。

举例:

  1. 用户和订单就是一对多

    一个用户可以下多个订单

  2. 订单和用户就是多对一

    多个订单属于同一个用户

  3. 人和身份证号就是一对一

    一个人只能有一个身份证号

    一个身份证号只能属于一个人

  4. 老师和学生之间就是多对多

    一个学生可以被多个老师教过

    一个老师可以交多个学生

特例:

  • 在 mybatis 中只有一对一一对多,它将多对多当成了多个 一对多,而多对一就是多个 一对一

一对一的查询操作

示例:用户和账户

  • 一个用户可以有多个账户
  • 一个账户只能属于一个用户(多个账户也可以属于同一个用户)

关系:本例以account表为主体,表现为多对一的关系,即:多个一对一!

根据以上关系建两张表:

user表的id是account表的UID的外键

需求:查询所有账户,同时还要获取到当前账户的所属用户信息

首先得写好SQL语句,SQL语句写好,后面就好办了:

select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;

执行结果:

SQL解释:

  • u和a分别是user表和account表的别名,SQL语句中FROM 执行的优先级别高于SELECT ,所以在FROM 后声明的别名,SELECT 可以用
  • SELECT u.* 代表查询user表下的所有字段;同理SELECT a.id,a.uid 就是只查询account表的id和uid字段
  • a.id as aid 也是给account表的id起别名 aid

根据ORM思想,查询之后,肯定要把查询的结果封装到实体类中,因为我们的查询涉及到两个实体类,所以在创建实体类的时候要在Account实体类中加个User实体类的属性,再设置Getter和Setter方法,Like this:

private Integer id;
private Integer uid;
private Double money;

private User user;

在XML映射文件中也要包含对User的封装:

<!-- 定义封装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的内容-->
    <!-- javaType是表示实体类为user且数据库字段为uid的数据封装到哪个实体类中 -->

    <association property="user" column="uid" javaType="user">
        <id property="id" column="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>
    </association>
</resultMap>

XML查询语句:

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

一对多的查询操作

简单来讲就是映射文件封装实体类时,<association> 标签改为:<collection> 标签,javaType 改为 ofType

12.JNDI

导入数据源的一种方式

项目实例:Mybatis的项目实例

13.Mybatis的延迟加载

13.1 什么是延迟加载?

场景:有时候一个账户有很多关联的信息,查询的时候会把关联的其他信息也查出来,这样对内存来说,就会造成系统资源浪费。

延迟加载:在真正使用数据时才发起查询,不用的时候不查询。这又叫 按需加载 或 懒加载。

13.2 什么是立即加载?

立即加载:不管用不用,只要一调用方法,立马发起查询。

13.3 对应关系

在对应的四中表关系中:

  • 一对多、多对多:通常情况下,都是采用延迟加载。
  • 多对一、一对一:通常情况下,都是采用立即加载。

一对一延迟加载实现

<association> 标签中的属性 select 来指定Dao中要延迟加载的方法的全类名

在主配置文件中开启延迟加载:

<configuration>
    <settings>
        <!--开启Mybatis支持延迟加载-->
        <!-- 默认为false -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 默认为false,3.4.1之前默认为true -->
        <setting name="aggressiveLazyLoading" value="false">
    </settings>
</configuration>

一对多延迟加载实现

同样,在<collection> 标签中指定Dao中要延迟加载的方法的全类名

之后再开启延迟加载即可!

Mybatis的缓存

  • 什么是缓存?

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

  • 为什么使用缓存?

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

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

    • 适用于缓存:
      • 经常查询,并且不经常改变
      • 数据的正确与否对最终结果影响不大(因为与数据库不同步)
    • 不适用于缓存:
      • 经常改变的数据
      • 数据的正确与否对最终结果影响很大
        • 例如:商品的库存,银行的汇率,股市的牌价等

一级缓存

  • 它指的是Mybatis中SqlSession对象的缓存。

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

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

  • 当SqlSession对象被close时,mybatis的一级缓存也就消失了。

    • 手动清空缓存的方法:用SqlSession对象调用clearCache()
  • 当调用SqlSession的修改、添加、删除、commit()、close()等方法时,Mybatis会自动清空一级缓存

  • 一级缓存存的是对象,查询相同数据,拿出来的对象都相等

二级缓存

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

二级缓存使用步骤:

  1. 让Mybatis框架支持二级缓存(在主配置文件中配置)

    <configuration>
        <settings>
            <!-- 其默认值为true -->
            <setting name="cacheEnable" value="true">
        </settings>
    </configuration>
  2. 让当前的映射文件支持二级缓存(在映射文件中配置)

    <mapper>
        <!-- 开启user支持二级缓存 -->
        <cache />
    </mapper>
  3. 让当前的操作支持二级缓存(在select标签中配置)

    <mapper>
        <select id="findAll" resultType="user" useCache="true">
            select * from user
        </select>
    </mapper>

二级缓存存的是数据,查询相同数据,拿出来的对象不同

14.Mybatis注解开发

注解开发,可以把映射配置文件去掉,但是主配置文件是必需的!

14.1 环境搭建

删掉映射文件,将主配置文件中的<mappers> 中的<mapper> 改为 <package> ,并用其name 属性指定dao接口的全类名,之后就可以使用注解开发。

14.2 注解开发注意事项

当两种配置方式(XML和注解)都存在时,Mybatis不知道该用哪种方式,它会报错!不管<mappers> 标签中如何指定!

可以把映射文件放在其他路径(dao接口以外的路径),或者直接删掉!

14.3 CRUD操作

增删改查 四个注解:

@Select@Insert@Delete@Update

都是放在dao接口中的 增删改查方法 的上面

注解的参数就是之前的Sql语句

原理:这四个注解,它们会自动获取方法的返回值(resultType)、参数(parameterType)以及接口所在的包(namespace)以及方法名(id),这样就完成了XML的功能

14.4 实体类属性与数据库表字段的对应关系

如果实体类中的属性与数据库中的字段名称不一样的话,Mybatis是无法完成封装的,所以,需要将其对应关系声明一下,再拿来用

XML方式,Mybatis有<resultMap> 标签可以解决

那么注解方式,自然也有解决方案:@Results

介绍:

  • @Results

    • 标签:@Results

    • 位置:在Dao接口的方法的上方即可

    • 属性:id 是整个对应关系的唯一标示id;value 是一个数组,对应关系都在value里面声明

    • value里还有一个标签:@Result 用于指定具体的对应关系

  • @Result

    • 在XML里有一个标签 <id> 专门用于指定主键字段的对应关系,注解也一样有。
    • 属性 id: 布尔类型,默认为false;当为true时,表示当前注解配置的是主键字段的对应关系;为false,反之。
    • 属性 column:指定数据库中的字段
    • 属性 property:指定实体类中的属性

eg:

@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();

别的方法想引用该关系怎么办?

使用 @ResultMap注解 其属性是String[] value,是一个数组,也就是说可以配置多个关系

标准写法:@ResultMap(value={"userMap"})

当只有一个value值得时候,可以省略不写:@ResultMap({"userMap"})

当数组只有一个值时,又可以省略大括号,所以:@ResultMap("userMap")

一对一查询配置

简单来说,一对一查询就是配置好查询出来的结果,将其封装到对应的实体类中去。

在14.4的基础上,再加个 one,Like this:

其中 select 用于指定通过column指定输的uid来当做条件,用findById来查询出user,fetchType 是来指定 立即加载还是延迟加载,EAGER是立即,LAZY是延迟;一般一对一是 立即加载,一对多是 延迟加载。

@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="xyz.sky03.dao.IUserDao.findById",fetchType= FetchType.EAGER))

})
List<Account> findAll();

一对多查询配置

一对多也是同理,要新建一个findAccountByUid方法, one 要改成 many ,一对多用懒加载

@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 = "xyz.sky03.dao.IAccountDao.findAccountByUid",

                            fetchType = FetchType.LAZY))
})
List<User> findAll();

二级缓存配置

不管XML还是注解,一级缓存都是自动开启的!

二级缓存配置:

  1. 在主配置文件中开启二级缓存

    <configuration>
        <!--配置开启二级缓存-->
        <settings>
            <!-- cacheEnabled的默认值就是true -->
    
            <setting name="cacheEnabled" value="true"/>
        </settings>
    </configuration>
  2. 在Dao类上加注解 @CacheNamespace ,blocking默认值false

    @CacheNamespace(blocking = true)
    public interface IUserDao {
        ...
    }

文章作者: Sky03
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Sky03 !
评论
 上一篇
失踪人口回归系列 失踪人口回归系列
缘由因为要专升本,不得不放弃继续自学java。 现在但是仍然抑制不住想折腾的内心。然后无意间看到了一个很漂亮的Hexo主题:matery。 (其实早就想换掉烂大街的Next,但是懒得折腾,又加上没时间。) 国人开发的主题,配置文件有中文说明
2019-10-14
下一篇 
Oracle Oracle
Oracle数据库的安装我安装的Oracle数据库版本为Oracle Database 12c Release 2 具体安装教程可以参考 百度经验 操作系统为安装在虚拟机里面的Windows Server 2019 DataCenter 客
2019-04-20
  目录