Mybatis项目小demo

Mybatis项目搭建

1.pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
   <!--项目名称,定义为组织名+项目名,类似包名-->
    <groupId>com.mytest</groupId>
    <!-- 模块名称 -->
    <artifactId>mybatis01</artifactId>
    <!-- 当前项目版本号,snapshot 为快照版本即非正式版本,release 为正式发布版本 -->
    <version>1.0-SNAPSHOT</version>
    <!--打包类型,jar:执行 package 会打成 jar 包;war:执行 package 会打成 war 包;pom :用于 maven 工程的继承,通常父工程设置为 pom-->
    <!--不写默认为jar包,注意使用内置插件,一定要打包为war包-->
    <packaging>war</packaging>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <!--servlet相关-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!--jsp页面相关-->
        <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <!--测试相关-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!--Mybatis框架入门开始-->
        <!--mybatis相关-->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <!--mysql连接-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--日志相关-->
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!--注解相关-->
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <!-- 解析 xml 的 dom4j -->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!-- dom4j 的依赖包 jaxen -->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- jdk1.8,需要设置编译版本为 1.8-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!--tomcat7插件,port端口号,path访问路径,例如:localhost:8080/(默认访问index页面)-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8080</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.mybatis-config文件

<?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>
    <!-- 开启延迟加载的支持 -->
    <!-- 打印日志-->
    <settings>
        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。默认:true  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- sql 打印到控制台 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- mybatis sql打印到log4j文件-->
        <!--<setting name="logImpl" value="LOG4J"/>-->
        <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="jdbcTypeForNull" value="NULL"/>
        <!-- 开启二级缓存的支持 -->
        <!--因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
        false 代表不开启二级缓存。-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <!-- 单个别名定义 -->
        <typeAlias alias="user" type="com.entity.User"/>
         <!-- 多个别名定义,默认为当前类类名大小写 -->
        <package name="com.entity"/>
    </typeAliases>
    <!--配置mybatis的环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
            <!--在db.properties文件中定义的话,可以使用以下动态方式配置-->
            <!--
              <properties name="resources" value="classpath*:db.properties"></properties>
             <dataSource type="POOLED">
                <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>

    <!--使用xml方式:告知 mybatis 映射配置的位置-->
    <mappers>
        <!--扫描单个配置文件-->
        <mapper resource="com/dao/IUserDao.xml"></mapper>
        <!--直接扫描整个com/dao下的所有配置文件-->
        <!--<package name="com.dao"/>-->
    </mappers>
    
    <!-- 使用注解方式:接口使用注解的时候,记得要删除IUserDao.xml文件。mybatis 映射配置的位置 -->
    <!--<mappers>
        <mapper class="com.dao.IUserDao"/>
    </mappers>-->
</configuration>

3.Mybatis的xml方式

1. User类

public class User implements Serializable {
	     private Integer id;
         private String username;
	     private Date birthday;
	     private String sex;
	     private String address;
	     此处省略getter和setter方法........
}

2. UserDao接口类

public interface IUserDao {
    /*
    查询所有用户
    */
    List<User> findAll();
 }  

3. UserTest测试类

public class MybastisCRUDTest {
    private InputStream in ;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before//在测试方法执行完成之前执行
    public void init ()throws IOException {
        //1.读取配置文件
        in= Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建构建者对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //3.创建 SqlSession 工厂对象
        factory = sqlSessionFactoryBuilder.build(in);
        //4.创建 sqlSession对象
        session = factory.openSession();
        //4.创建 SqlSession 对象----不手动提交事务
        //session = factory.openSession(true);
        //5.创建 Dao的代理对象-----流行方式
        userDao = session.getMapper(IUserDao.class);
        //5.创建 Dao 接口的实现类----传统方式
        //userDao = new UserDaoImpl(factory);
    }
    @After//在测试方法执行完成之后执行
    public void destroy() throws Exception{
        session.commit();//实现增删改时一定要去控制事务的提交
        //7.释放资源
        session.close();
        in.close();
    }
    @Test
    public void testFindAll(){
        //6.执行操作
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }
 }

4.mybatis配置解析

1.#{}与${}的区别
  #{}表示一个占位符号
  通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,#{}可以有效防止 sql 注入。
  #{}可以接收简单类型值或 pojo 属性值。
  如果 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。

  ${}表示拼接 sql 串
  通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换,
  ${}可以接收简单类型值或 pojo 属性值,
  如果 parameterType 传输单个简单类型值,${}括号中只能是 value。

2.模糊查询的${value}源码分析
   TextSqlNode 类的源码。源码中指定了读取的 key 的名字就是”value”,所以我们在绑定参数时就只能叫 value 的名字了

3.Mybatis和jdbc比较
  1.数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
  解决:
      在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。
  2.Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
  解决:
      将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。
  3.向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。
  解决:
      Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的
      类型。
  4.对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对
  象解析比较方便。
  解决:
      Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的
      类型。
      
4.parameterType 配置参数
  基 本 类 型 和 String 我 们 可 以 直 接 写 类 型 名 称 , 也 可 以 使 用 包 名 . 类 名 的 方 式 , 例 如 :
  java.lang.String。
  实体类类型,目前我们只能使用全限定类名。
  究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,
  而我们的是实体类并没有注册别名,所以必须写全限定类名。

5.resultType 配置结果类型
  resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
  如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。
  注意:实体类中的属性名称必须和查询语句中的列名保持一致,否则无法实现封装。

6.实体类中username和userName都能获取到数据库的对应值
  mysql 在 windows 系统中不区分大小写!
  写法:  select
              id as userId,
              username as userName,
              birthday as userBirthday,
              sex as userSex,
              address as userAddress
          from user
  查询很多,都使用别名的话,很麻烦。需要用到resultMap

7.resultMap 结果类型
  resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。
  在 select 标签中使用 resultMap 属性指定引用即可。
  同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。

8.使用 Mybatis 开发 Dao,通常有两个方法,即原始 Dao 开发方式和 Mapper 接口代理开发方式。Mapper 接口代理开发方式是主流

9.sqlMapConfig.xml配置标签用法
  sqlMapConfig配置顺序:
              -properties(属性)
              --property
              -settings(全局配置参数)
              --setting
              -typeAliases(类型别名)
              --typeAliase
              --package
              -typeHandlers(类型处理器)
              -objectFactory(对象工厂)
              -plugins(插件)
              -environments(环境集合属性对象)
              --environment(环境子属性对象)
              ---transactionManager(事务管理)
              ---dataSource(数据源)
              -mappers(映射器)
              --mapper
              --package
  properties:用两种方式指定属性配置
  第一种:
              <property name="driver" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql://localhost:3306/test"/>
              <property name="username" value="root"/>
              <property name="password" value="root"/>
  第二种:
              在 classpath 下定义 db.properties 文件
              jdbc.driver=com.mysql.jdbc.Driver
              jdbc.url=jdbc:mysql://localhost:3306/test
              jdbc.username=root
              jdbc.password=root

  typeAliases(类型别名)
               <typeAliases>
                    <!-- 单个别名定义 -->
                    <typeAlias alias="user" type="com.entity.User"/>
                    <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
                   <package name="com.entity"/>
                   <package name="其它包"/>
               </typeAliases>

  mappers(映射器)
        1.使用相对于类路径的资源
            如:<mapper resource="com/dao/IUserDao.xml" />
        2.使用 mapper 接口类路径
            如:<mapper class="com.dao.IUserDao"/>
            注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
        3.注册指定包下的所有 mapper 接口
            如:<package name="com.dao"/>
            注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。

10.Mybatis 中我们将它的数据源 dataSource
        可以看出 Mybatis 将它自己的数据源分为三类:
        UNPOOLED 不使用连接池的数据源
        POOLED 使用连接池的数据源
        JNDI 使用 JNDI 实现的数据源

11. Mybatis 自动提交事务的设置
        setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。
    问:为什么 CUD 过程中必须使用 sqlSession.commit()提交事务?
    答:主要原因就是在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们就必须使用
        sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法实现事务提交。

12. Mybatis 的动态 SQL 语句
    <if>标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。另外要注意 where 1=1 的作用
        <select id="findByUser" resultType="user" parameterType="user">
            select * from user where 1=1
            <if test="username!=null and username != '' ">
                and username like #{username}
            </if>
            <if test="address != null">
                and address like #{address}
            </if>
        </select>
13.为了简化上面 where 1=1 的条件拼装,我们可以采用<where>标签来简化开发。
    <!-- 根据用户信息查询 -->
    <select id="findByUser" resultType="user" parameterType="user">
         <include refid="defaultSql"></include>
         <where>
             <if test="username!=null and username != '' ">
                and username like #{username}
             </if>
             <if test="address != null">
                and address like #{address}
             </if>
        </where>
    </select>

14.动态标签之<foreach>标签
   传入多个 id 查询用户信息,用下边两个 sql 实现:
   SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
   SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16)
   这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。

   <select id="findInIds" resultType="user" parameterType="queryvo">
         <!-- select * from user where id in (1,2,3,4,5); -->
         <include refid="defaultSql"></include>
         <where>
              <if test="ids != null and ids.size() > 0">
                   <foreach collection="ids" open="id in ( " close=")" item="uid" separator=",">
                   #{uid}
                   </foreach>
              </if>
         </where>
   </select>
   SQL 语句:
       select 字段 from user where id in (?)
   <foreach>标签用于遍历集合,它的属性:
   collection:代表要遍历的集合元素,注意编写时不要写#{}
   open:代表语句的开始部分
   close:代表结束部分
   item:代表遍历集合的每个元素,生成的变量名
   sperator:代表分隔符

15.Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
    <!-- 抽取重复的语句代码片段 -->
        <sql id="defaultSql">
            select * from user
        </sql>
    <!-- 配置查询所有操作 -->
    <select id="findAll" resultType="user">
        <include refid="defaultSql"></include>
    </select>

16.一对一查询
   第一种方式:新增A类 extends 原始A类 (新增A类属性为B类想要查询的属性)
               定义A类的接口类  ,查询方法返回类型为:新增A类类型
               xml中resultType="新增A类类型"
   第二种方式:原始A类 添加 B类对象
               A类的接口类  ,查询方法返回类型为:A类类型
               xml中resultType="A类类型"
               association:用以一对一关系的引用
               <!-- 建立对应关系 -->
                   <resultMap type="account" id="accountMap">
                       <id column="aid" property="id"/>
                       <result column="uid" property="uid"/>
                       <result column="money" property="money"/>
                       <!-- 它是用于指定从表方的引用实体属性的 -->
                       <association property="user" javaType="user">
                           <id column="id" property="id"/>
                           <result column="username" property="username"/>
                           <result column="sex" property="sex"/>
                           <result column="birthday" property="birthday"/>
                           <result column="address" property="address"/>
                       </association>
                   </resultMap>
                   <select id="findAlls" resultMap="accountMap">
                       select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;
                   </select>

17.一对多查询
   方式:原始A类 添加 B类对象
                  A类的接口类  ,查询方法返回类型为:A类类型
                  xml中resultType="A类类型"
                  collection 是用于建立一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型
                  collection
                        部分定义了用户关联的账户信息。表示关联查询结果集
                  property="accounts":
                        关联查询的结果集存储在 User 对象的上哪个属性。
                  ofType="account":
                        指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

                  <resultMap type="user" id="userMap">
                          <id column="id" property="id"></id>
                          <result column="username" property="username"/>
                          <result column="address" property="address"/>
                          <result column="sex" property="sex"/>
                          <result column="birthday" property="birthday"/>
                          <!-- collection 是用于建立一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型-->
                          <collection property="accounts" ofType="account">
                              <id column="aid" property="id"/>
                              <result column="uid" property="uid"/>
                              <result column="money" property="money"/>
                          </collection>
                      </resultMap>
                      <!-- 配置查询所有操作 -->
                      <select id="findAllUserAccount" resultMap="userMap">
                          select
                              u.*,
                              a.id as aid ,
                              a.uid,
                              a.money
                          from
                              user u
                          left outer join
                              account a
                          on
                              u.id =a.uid
                      </select>

18.Mybatis延迟加载
   延迟加载:
   就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
   好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速
   度要快。
   坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗
   时间,所以可能造成用户等待时间变长,造成用户体验下降。

19.association、collection 具备延迟加载功能
   开启 Mybatis 的延迟加载策略:在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置
   <settings>
       <setting name="lazyLoadingEnabled" value="true"/>
       <setting name="aggressiveLazyLoading" value="false"/>
   </settings>
   只查账户信息不查用户信息,association不加载;
   只查用户信息不查账户信息,collection不加载;

20.Mybatis 一级缓存
   一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
   在测试中,我们查询了两次,但最后只执行了一次数据库操作,这就是 Mybatis 提供给我们的一级缓存在起作用了。
   因为一级缓存的存在,导致第二次查询 id 为 41 的记录时,并没有发出 sql 语句。从数据库中查询数据,而是从一级缓存中查询

   注意:写sql语句,要配置开启缓存useCache="true"

   一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存

   原因:
       第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查
       询用户信息。
       得到用户信息,将用户信息存储到一级缓存中。
       如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样
       做的目的为了让缓存中存储的是最新的信息,避免脏读。
       第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存
       中获取用户信息

21.缓存的同步
   当执行sqlSession.close()后,再次获取sqlSession并查询id=1的User对象时,又重新执行了sql
   语句,从数据库进行了查询操作。

22.Mybatis二级缓存
   二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个
   SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

   首先开启 mybatis 的二级缓存。
   sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
   如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交,将会清空该 mapper 映射下的二
   级缓存区域的数据。
   sqlSession2 去查询与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从
   缓存中取出数据。

23.二级缓存的开启与关闭
   第一步:在 SqlMapConfig.xml 文件开启二级缓存
   <settings>
   <!-- 开启二级缓存的支持 -->
        <setting name="cacheEnabled" value="true"/>
   </settings>
   因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
   false 代表不开启二级缓存。

   第二步:配置相关的 Mapper 映射文件
   <cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
   <?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.dao.IUserDao">
       <!-- 开启二级缓存的支持 -->
       <cache></cache>
    </mapper>

    第三步:配置 statement 上面的 useCache 属性
    <!-- 根据 id 查询 -->
    <select id="findById" resultType="user" parameterType="int" useCache="true">
        select * from user where id = #{uid}
    </select>
    将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用
    二级缓存,如果不使用二级缓存可以设置为 false。
    注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

    经过测试:
        /**
         * 测试一级缓存
         */
         @Test
         public void testFirstLevelCache(){
         SqlSession sqlSession1 = factory.openSession();
         IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
         User user1 = dao1.findById(41);
         System.out.println(user1);
         sqlSession1.close();//一级缓存消失
         SqlSession sqlSession2 = factory.openSession();
         IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
         User user2 = dao2.findById(41);
         System.out.println(user2);
         sqlSession2.close();
         System.out.println(user1 == user2);
         }

    结论:经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
          次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
    注意:当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化
          方式来保存对象。

4.Mybatis注解方式

1.User类

public class User  implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Date getUserBirthday() {
        return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }

    public String getUserSex() {
        return userSex;
    }

    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userBirthday=" + userBirthday +
                ", userSex='" + userSex + '\'' +
                ", userAddress='" + userAddress + '\'' +
                '}';
    }
}

2.UserDao接口类

//通过注解方式,我们就不需要再去编写 UserDao.xml 映射文件了。
public interface IUserDao2 {
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",
            value= {
                    @Result(id=true,column="id",property="userId"),
                    @Result(column="username",property="userName"),
                    @Result(column="sex",property="userSex"),
                    @Result(column="address",property="userAddress"),
                    @Result(column="birthday",property="userBirthday")
            })
    List<User1> findAll();

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

    /**
     * 保存操作
     * @param user
     * @return
     */
    @Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")
    @SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before =
            false, statement = { "select last_insert_id()" })
    int saveUser(User user);

    /**
     * 更新操作
     * @param user
     * @return
     */
    @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id =#{id} ")
    int updateUser(User user);

    /**
     * 删除用户
     * @param userId
     * @return
     */
    @Delete("delete from user where id = #{uid} ")
    int deleteUser(Integer userId);
    /**
     * 查询使用聚合函数
     * @return
     */
    @Select("select count(*) from user ")
    int findTotal();

    /**
     * 模糊查询
     * @param name
     * @return
     */
    @Select("select * from user where username like #{username} ")
    List<User> findByName(String name);
}

3.mybatis-config配置文件

<?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 文件的位置 -->
    <properties resource="db.properties"></properties>
    <!-- 配置别名的注册 -->
    <typeAliases>
        <package name="com.entity"/>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置 mysql 的环境 -->
        <environment id="mysql">
            <!-- 配置事务的类型是 JDBC -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <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>
    <!-- 配置映射信息 -->
    <mappers>
        <!-- 配置 dao 接口的位置,它有两种方式
        第一种:使用 mapper 标签配置 class 属性
        第二种:使用 package 标签,直接指定 dao 接口所在的包
        -->
        <package name="com.dao"/>
    </mappers>
</configuration>

4.db.properties

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

5.userTest类

public class MybastisCRUDTest {
    private InputStream in ;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before//在测试方法执行完成之前执行
    public void init ()throws IOException {
        //1.读取配置文件
        in= Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.创建构建者对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //3.创建 SqlSession 工厂对象
        factory = sqlSessionFactoryBuilder.build(in);
        //4.创建 sqlSession对象
        session = factory.openSession();
        //4.创建 SqlSession 对象----不手动提交事务
        //session = factory.openSession(true);
        //5.创建 Dao的代理对象-----流行方式
        userDao = session.getMapper(IUserDao.class);
        //5.创建 Dao 接口的实现类----传统方式
        //userDao = new UserDaoImpl(factory);
    }
    @After//在测试方法执行完成之后执行
    public void destroy() throws Exception{
        session.commit();//实现增删改时一定要去控制事务的提交
        //7.释放资源
        session.close();
        in.close();
    }
    /**
     * 测试查询所有
     */
    @Test
    public void testFindAll() {
        List<User> users = userDao.findAll();
        for(User user : users) {
            System.out.println(user);
        } }
    /**
     * 测试查询一个
     */
    @Test
    public void testFindById() {
        User user = userDao.findById(3);
        System.out.println(user);
    }

    /**
     * 测试保存
     */
    @Test
    public void testSave() {
        User user = new User();
        user.setUserName("mybatis annotation");
        user.setUserSex("男");
        user.setUserAddress("北京市顺义区");
        user.setUserBirthday(new Date());
        int res = userDao.saveUser(user);
        System.out.println("影响数据库记录的行数:"+res);
        System.out.println("插入的主键值:"+user.getUserId());
    }

    /**
     * 测试更新
     */
    @Test
    public void testUpdate() {
        User user = userDao.findById(3);
        user.setUserBirthday(new Date());
        user.setUserSex("女");
        int res = userDao.updateUser(user);
        System.out.println(res);
    }
    /**
     * 测试删除
     */
    @Test
    public void testDelete() {
        int res = userDao.deleteUser(3);
        System.out.println(res);
    }
    /**
     * 测试查询使用聚合函数
     */
    @Test
    public void testFindTotal() {
        int res = userDao.findTotal();
        System.out.println(res);
    }

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

6.mybatis 的常用注解

1.常用注解
    @Insert:实现新增
    @Update:实现更新
    @Delete:实现删除
    @Select:实现查询
    @Result:实现结果集封装
    @Results:可以与@Result 一起使用,封装多个结果集
    @ResultMap:实现引用@Results 定义的封装
    @One:实现一对一结果集封装
    @Many:实现一对多结果集封装
    @SelectProvider: 实现动态 SQL 映射
    @CacheNamespace:实现注解二级缓存的使用
   @Results 注解
        代替的是标签<resultMap>
        该注解中可以使用单个@Result 注解,也可以使用@Result 集合
        @Results({@Result(),@Result()})或@Results(@Result())

            @Result 注解
            代替了 <id>标签和<result>标签
            @Result 中 属性介绍:
            id 是否是主键字段
            column 数据库的列名
            property 需要装配的属性名
            one 需要使用的@One 注解(@Result(one=@One)()))
            many 需要使用的@Many 注解(@Result(many=@many)()))

              1. @One 注解(一对一)
               代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
               @One 注解属性介绍:
               select 指定用来多表查询的 sqlmapper
               fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。。
               使用格式:
               @Result(column=" ",property="",one=@One(select=""))

              2.@Many 注解(多对一)
               代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
               注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType
               (一般为 ArrayList)但是注解中可以不定义;
               使用格式:
               @Result(property="",column="",many=@Many(select=""))

2.例子
   1.一对一
                 /**
                 * 查询所有账户,采用延迟加载的方式查询账户的所属用户
                 * @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(column="uid",
                                        property="user",
                                        one=@One(select="com.dao.IUserDao.findById",
                                                fetchType= FetchType.LAZY) )
                        })
                List<Account> findAll();
   2.多对一
       /**
        * 查询所有用户以及对应的所有账户
        * @return
        */
       @Select("select * from user")
       @Results(id="userMap",
               value= {
                       @Result(id=true,column="id",property="userId"),
                       @Result(column="username",property="userName"),
                       @Result(column="sex",property="userSex"),
                       @Result(column="address",property="userAddress"),
                       @Result(column="birthday",property="userBirthday"),
                       @Result(column="id",property="accounts",
                               many=@Many(
                                       select="com.dao.IAccountDao.findByUid",
                                       fetchType= FetchType.LAZY
                               ) )
               })
       List<User> findAll();

       @Many:
       相当于<collection>的配置
       select 属性:代表将要执行的 sql 语句
       fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值

3..基于注解的缓存  
    1.在sqlMapConfig.xml中配置
    <!-- 配置二级缓存 --> 
    <settings>
    <!-- 开启二级缓存的支持 --> 
        <setting name="cacheEnabled" value="true"/>
    </settings>
    2.在持久层接口中使用注解配置二级缓存
    /**
    *
    * <p>Title: IUserDao</p>
    * <p>Description: 用户的持久层接口</p>
    * <p>Company: http://www.itheima.com/ </p>
    */
    @CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
    public interface IUserDao {}
    ```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值