SSM-MyBatis(四)-二级缓存、dao层实现

MyBatis的两级缓存

  1. 缓存:是指为了减少数据库直接访问次数、提高访问效率,而临时存储在内存中的数据.
    1. 数据库是存储在硬盘上的文件,每次查询都进行了IO操作,效率较低
  2. 适合存放到缓存中的数据:经常查询、不经常改变、数据的正确性对最终结果影响不大
  3. 举例一些不适合放到缓存中的数据:商品库存、股票 黄金的价格、汇率
  4. MyBatis的缓存分为一级缓存、二级缓存,用于缓存select的结果
    1. 参考资料:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache

一级缓存

  1. 一级缓存是存放到了SqlSession对象中
    1. 同一个SqlSession的select共享缓存
    2. 所以当关闭SqlSession时,缓存也就失效了
    3. 执行insert、update、delete、commit等方法时,会自动清理一级缓存
    4. 由于在很多时候,每次查询用的都是不同的SqlSession,所以一级缓存的命中率并不高
  2. 一级缓存代码举例:

     public void firstLevel() {
         try (SqlSession session = MyBatises.openSession()) {
             Job job1 = session.selectOne("job.get", 1);
             System.out.println(job1);
             Job job2 = session.selectOne("job.get", 1);
             System.out.println(job2);
             //job1与job2打印一样
         }
         //不同session,关闭SqlSession时,缓存也就失效了
         try (SqlSession session = MyBatises.openSession()) {
             Job job3 = session.selectOne("job.get", 1);
             System.out.println(job3);
             //job1与job3打印不一样,不同的session
         }
         //执行insert、update、delete、commit等方法时,会自动清理一级缓存
         try (SqlSession session = MyBatises.openSession()) {
             Job job4 = session.selectOne("job.get", 1);
             System.out.println(job4);
             //会清除一级缓存
             session.update("job.update", null);
             Job job5 = session.selectOne("job.get", 1);
             System.out.println(job5);
             //job4与job5打印不一样
         }
     }
    

二级缓存

  1. 为了提高缓存的命中率,可以考虑开启MyBatis的二级缓存,它是namespace(mapper)级别的缓存
    1. 同一个namespace下的select共享缓存
    2. 默认情况,namespace下update、insert、delete执行成功时,会自动清理二级缓存
    3. 当调用SqlSession的close方法时,会将查询结果放进二级缓存

开启二级缓存

  1. 在mybatis-config.xml中配置

     <setting name="cache Enabled" value="true"/>
    
  2. 在映射文件的mapper中添加cache标签,默认会缓存映射文件中的所有select的结果

     //job.xml
     <mapper namespace="job">
         <cache readOnly="false"/>
        
         <sql id="sqlListAll">
             SELECT * FROM job
         </sql>
        
         <select id="list" resultType="zh.bean.Job">
             <include refid="sqlListAll"/>
         </select>
        
         <select id="get" parameterType="int" resultType="zh.bean.Job">
             <include refid="sqlListAll"/> WHERE id = #{id}
         </select>
        
         <update id="update" parameterType="zh.bean.Job">
             UPDATE job SET name = #{name}, duty = #{duty} WHERE id = #{id}
         </update>
     </mapper>
    
  3. cache标签的常用属性有
    1. size:缓存多少个存储结果(单个对象或一个列表都属于一个引用) 的引用,默认值是1024
    2. eviction:当缓存数量超过size时的清除策略。可选值有LRU(默认值)、FIFO、SOFT、WEAK
      1. LRU – 最近最少使用:移除最长时间不被使用的对象。
      2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
      3. SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
      4. WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
    3. flush Interval:每隔多少毫秒清除一次缓存, 默认不会定时清除缓存
    4. readOnly: true代表缓存的是对原对象的引用,false代表缓存的是原对象序列化后的拷贝对象
      1. 所以false时要求bean实现Serializable接口。默认值是false
      2. readOnly为true,代表:只读,二级缓存中是:原对象,不安全,会修改原来对象的值
      3. readOnly为false,代表:可读写,二级缓存中是:原对象序列化后的结果(相当于深拷贝),安全,不会修改原来对象的值

         package zh.bean;
         import java.io.Serializable;
         //bean实现Serializable接口
         public class Job implements Serializable {
             private Integer id;
             private String name;
             private String duty;
                    
             //get、set方法,略
         }
        
    5. 代码举例:
       //Job.xml中
       <cache
         eviction="FIFO"
         flushInterval="60000"
         size="512"
         readOnly="true"/>
      
  4. 测试代码举例

     public void secondLevel() {
         try (SqlSession session = MyBatises.openSession()) {
             Job job1 = session.selectOne("job.get", 1);
             job1.setName("ZH666");
             System.out.println(job1 + "_" + job1.getName());
         }
    
         try (SqlSession session = MyBatises.openSession()) {
             Job job2 = session.selectOne("job.get", 1);
             System.out.println(job2 + "_" + job2.getName());
         }
    
         //readOnly为true,则job1与job2为同一个对象
         //readOnly为false,则job1与job2不是同一个对象,但是job2的name也为ZH666
         //原因:只有session在调用close的时候才会将数据放入缓存,所以job1.setName("ZH666");是在放入缓存之前设置的,因此job2从缓存中复制出的name也为ZH666
     }
    
  5. 总结:放入二级缓存的时机就是session调用close方法时

userCache、flushCache

  1. 可以通过设置userCache属性来决定某个select是否需要开启二级缓存
    1. 注意:前提条件是全局必须开启二级缓存,而且映射文件的mapper中添加cache标签。
     <select id="get" useCache="false" parameterType="int" resultType="zh.bean.Job">
         <include refid="sqlListAll"/> WHERE id = #{id}
     </select>
    
  2. 可以通过设置flushCache属性来决定某个操作之后,是否需要清除缓存

     <update id="update" flushCache="false" parameterType="zh.bean.Job">
         UPDATE job SET name = #{name}, duty = #{duty} WHERE id = #{id}
     </update>
    

使用MyBatis实现dao层

  1. 使用MyBatis实现dao层的几种方式
    1. 方法一: 自定义dao实现类impl,在实现中通过SqlSession实现API(使用XML)
    2. 方法二:只定义dao接口类,SqlSession的getMapper方法生成dao的代理对象(使用XML)impl,然后通过daoimpl直接调用API(daoimpl不需要手动封装SqlSession调用API的代码)
    3. 方法三:只定义dao接口类,SqlSession的getMapper方法生成dao的代理对象 (使用注解)impl。
      1. 目前注解的功能并没有XML强大,所以也可以XML+注解混合使用

初始化项目

  1. 创建bean对象skill

     //com.zh.bean.Skill
     public class Skill extends Bean {
         private Integer id;
         private Date createdTime;
         private String name;
         private Integer level;
         //get/set方法略
         public Skill() {}
     }
    
  2. 引入druid第三方连接池
    1. pom.xml添加依赖
    2. 添加durid.properties文件

       dev.driverClass=com.mysql.jdbc.Driver
       dev.url=jdbc:mysql://localhost:3306/test-mybatis
       dev.username=root
       dev.password=root
       dev.initialSize=5
       dev.maxActive=10
       dev.maxWait=5000
      
  3. mybatis封装
    1. pom.xml引入mybatis
    2. 配置全局文件mybatis-config.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">
       <configuration>
           <properties resource="druid.properties" />
           <settings>
               <!-- name与value的内容在mysql官方可以查询:开启驼峰命名自动映射,比如从数据库列名my_age映射到Bean属性名myAge -->
               <setting name="mapUnderscoreToCamelCase" value="true"/>
           </settings>
           <!-- 别名 -->
           <typeAliases>
               <!-- 一旦设置了别名,它是不区分大小写的 -->
               <typeAlias type="com.zh.bean.Skill" alias="skill" />
               <typeAlias type="com.zh.common.DruidDataSourceFactory" alias="druid" />
           </typeAliases>
              
           <!-- 设置使用什么环境-->
           <environments default="development">
               <!-- 开发环境(调试阶段) -->
               <environment id="development">
                   <!-- 采用JDBC的事务管理方法 -->
                   <transactionManager type="JDBC" />
                   <dataSource type="DRUID">
                       <property name="driverClass" value="${dev.driverClass}"/>
                       <property name="url" value="${dev.url}"/>
                       <property name="username" value="${dev.username}"/>
                       <property name="password" value="${dev.password}"/>
                       <property name="initialSize" value="${dev.initialSize}"/>
                       <property name="maxActive" value="${dev.maxActive}"/>
                       <property name="maxWait" value="${dev.maxWait}"/>
                   </dataSource>
               </environment>
           </environments>
           <mappers>
               <mapper resource="mappers/skill.xml"/>
           </mappers>
              
       </configuration>
      
    3. 创建UnpooledDataSourceFactory子类,设置mybatis的数据源为durid

       //com.zh.common.DruidDataSourceFactory
       package com.zh.common;
       import com.alibaba.druid.pool.DruidDataSource;
       import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
       public class DruidDataSourceFactory extends UnpooledDataSourceFactory {
           public DruidDataSourceFactory() {
               this.dataSource = new DruidDataSource();
           }
       }
      
    4. 封装SqlSession

       //com.zh.util.MyBatises
       public class MyBatises {
           private static SqlSessionFactory factory;
           static {
               try (Reader reader  = Resources.getResourceAsReader("mybatis-config.xml")) {
                   // 创建一个工厂
                   factory = new SqlSessionFactoryBuilder().build(reader);
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
              
           public static SqlSession openSession(boolean autoCommit) {
               return factory.openSession(autoCommit);
           }
              
           public static SqlSession openSession() {
               return factory.openSession();
           }
       }
      

方法一:自定义dao实现类

  1. 添加mapper:skill.xml,添加增删该改查的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="skill">
         <sql id="sqlList">SELECT * FROM skill</sql>
        
         <select id="get" parameterType="int" resultType="Skill">
             <include refid="sqlList"/> WHERE id = #{id}
         </select>
        
         <select id="list" resultType="Skill">
             <include refid="sqlList"/>
         </select>
        
         <update id="update" parameterType="Skill">
             UPDATE skill SET name = #{name}, level = #{level} WHERE id = #{id}
         </update>
        
         <insert id="save"
                 useGeneratedKeys="true" keyProperty="id"
                 parameterType="Skill">
             INSERT INTO skill(name, level) VALUES(#{name}, #{level})
         </insert>
        
         <delete id="remove" parameterType="int">
             DELETE FROM skill WHERE id = #{id}
         </delete>
     </mapper>
    
  2. 新建dao层(dao层分为接口+实现)
    1. 接口SkillDao

       package com.zh.dao;
       import com.zh.bean.Skill;
       import java.util.List;
       public interface SkillDao {
           boolean save(Skill skill);
           boolean update(Skill skill);
           boolean remove(Integer id);
           Skill get(Integer id);
           List<Skill> list();
       }
      
    2. SkillDao接口实现SkillDaoImpl

       package com.zh.dao.impl;
       import com.zh.bean.Skill;
       import com.zh.dao.SkillDao;
       import com.zh.util.MyBatises;
       import org.apache.ibatis.session.SqlSession;
       import java.util.List;
       public class SkillDaoImpl implements SkillDao {
           @Override
           public boolean save(Skill skill) {
               try (SqlSession session = MyBatises.openSession(true)) {
                   return session.insert("skill.save", skill) > 0;
               }
           }
           @Override
           public boolean update(Skill skill) {
               try (SqlSession session = MyBatises.openSession(true)) {
                   return session.update("skill.update", skill) > 0;
               }
           }
           @Override
           public boolean remove(Integer id) {
               try (SqlSession session = MyBatises.openSession(true)) {
                   return session.delete("skill.remove", id) > 0;
               }
           }
           @Override
           public Skill get(Integer id) {
               try (SqlSession session = MyBatises.openSession()) {
                   return session.selectOne("skill.get", id);
               }
           }
           @Override
           public List<Skill> list() {
               try (SqlSession session = MyBatises.openSession()) {
                   return session.selectList("skill.list");
               }
           }
       }
      
  3. 测试代码SkillTest

     @Test
     public void get() {
         SkillDao dao = new SkillDaoImpl();
         System.out.println(dao.get(1));
     }
     @Test
     public void list() {
         SkillDao dao = new SkillDaoImpl();
         System.out.println(dao.list());
     }
     @Test
     public void save() {
         SkillDao dao = new SkillDaoImpl();
         Assert.assertTrue(dao.save(new Skill("mj888", 100)));
     }
     @Test
     public void update() {
         SkillDao dao = new SkillDaoImpl();
         Skill skill = new Skill("666", 99);
         skill.setId(80);
         Assert.assertTrue(dao.update(skill));
     }
     @Test
     public void remove() {
         SkillDao dao = new SkillDaoImpl();
         Assert.assertTrue(dao.remove(80));
     }
    
  4. 缺点: SkillDaoImpl代码过于累赘,如果有n个impl,那么不同的仅仅是“skill.list”等语句

方法二:getMapper+XML实现dao层

  1. 有2个配置要求
    1. mapper的namespace必须是dao接口类的全类名
    2. mapper中select、update、insert、delete的id值必须和dao的方法名一致
  2. 如果update、insert、delete方法的返回值是Boolean类型
    1. 代理对象内部是影响记录数大于0就返回true
    2. 参考源码:org.apache.ibatis.binding.MapperMethod.rowCountResult方法
  3. 通过SqlSession的getMapper方法生成对应dao接口的实现类-代理类,然后用代理类调用api

代码举例

  1. 删除dao的实现SkillDaoImpl
  2. 改造skil.xml
    1. namespace修改为:com.mj.dao.SkillDao
    2. 将里面对应的sql操作语句的id变为SkillDao接口对应的方法名
     <mapper namespace="com.zh.dao.SkillDao">
         <sql id="sqlList">SELECT * FROM skill</sql>
         <update id="update" parameterType="Skill">
             UPDATE skill SET name = #{name}, level = #{level} WHERE id = #{id}
         </update>
        
         <insert id="save"
                 useGeneratedKeys="true" keyProperty="id"
                 parameterType="Skill">
             INSERT INTO skill(name, level) VALUES(#{name}, #{level})
             <!--        <selectKey keyProperty="id" resultType="int" order="AFTER">-->
             <!--            SELECT LAST_INSERT_ID()-->
             <!--        </selectKey>-->
         </insert>
        
         <delete id="remove" parameterType="int">
             DELETE FROM skill WHERE id = #{id}
         </delete>
        
         <!--constructor: 指定调用有参数的构造方法,默认调用无参的构造方法-->
         <resultMap id="rmGet" type="Skill">
             <constructor>
                 <!--专门用来指定主键id映射-->
                 <!--<idArg></idArg>-->
                 <arg name="level" column="level"/>
                 <arg name="name" column="name"/>
             </constructor>
         </resultMap>
         <!--
         该句的本质是先sql查询数据,然后创建一个Skill对象,将数据映射给对象属性
         通常创建对象是直接调用Skill类的无参构造函数,但是也可以指定调用其他构造函数
         指定调用哪个构造函数通过<constructor>标签
         -->
         <select id="get" parameterType="int" resultMap="rmGet">
             <include refid="sqlList"/> WHERE id = #{id}
         </select>
        
         <select id="list" resultType="Skill">
             <include refid="sqlList"/>
         </select>
     </mapper>
    
    1. 注意:如果使用指定构造函数,bean对象的构造函数参数要添加@param修饰

       public Skill(@Param("name") String name, @Param("level") Integer level) {
           this.name = name;
           this.level = level;
       }
      
  3. 测试代码SkillTest

     @Test
     public void get() {
         try (SqlSession session = MyBatises.openSession()) {
             // 根据SkillDao接口,生成SkillDao的代理对象
             SkillDao dao = session.getMapper(SkillDao.class);
             System.out.println(dao.get(1));
         }
     }
    
     @Test
     public void list() {
         try (SqlSession session = MyBatises.openSession()) {
             // 生成SkillDao的代理对象
             SkillDao dao = session.getMapper(SkillDao.class);
             System.out.println(dao.list());
         }
     }
    
     @Test
     public void save() {
         try (SqlSession session = MyBatises.openSession(true)) {
             // 生成SkillDao的代理对象
             SkillDao dao = session.getMapper(SkillDao.class);
             Assert.assertTrue(dao.save(new Skill("mj888", 100)));
         }
     }
    
     @Test
     public void update() {
         try (SqlSession session = MyBatises.openSession(true)) {
             // 生成SkillDao的代理对象
             SkillDao dao = session.getMapper(SkillDao.class);
             Skill skill = new Skill("666", 99);
             skill.setId(80);
             Assert.assertTrue(dao.update(skill));
         }
     }
    
     @Test
     public void remove() {
         try (SqlSession session = MyBatises.openSession(true)) {
             // 生成SkillDao的代理对象
             SkillDao dao = session.getMapper(SkillDao.class);
             Assert.assertTrue(dao.remove(78));
         }
     }
    

方法三:getMapper+注解实现dao层

  1. 首先要在mybatis-config.xml中配置dao的位置
    1. 方法一:<mapper class="dao的全类名"/>
    2. 方法二:<packagename="dao所在的包"/>
  2. 常用注解

     @Select、@Insert、@Update、@Delete、@Selectedkey
     @Param:设置参数名,@Options:设置其他属性值,@CacheNamespace:对应<cache>
     @Results、@ResultMap:对应<resultMap>,@Result:对应<id>、<result>
     @One对应<association>,@Many对应<collection>
     @ConstructorArgs:对应<constructor>,@Arg:对应<idArg>、<arg>
    
  3. 其他知识
    1. 可以使用<script>嵌套其他XML标签中的内容
    2. 参考资料:https://mybatis.org/mybatis-3/zh/java-api.html
  4. 纯注解的优缺点
    1. 优点:sql语句直接写在代码中,简洁而且容易理解
    2. 缺点:项目编译后直接成为字节码class文件,无法手动修改。xml可在服务器手动修改sql语句
    3. 通常可以用xml与注解混合使用

代码举例

  1. 删除mappers以及skil.xml
  2. 在mybatis-config.xml文件中的mapper修改如下:

     <mapper class="com.zh.dao.SkillDao"/>
    
  3. 在SkillDao中使用注解

     //设置二级缓存,readWrite等价于readOnly
     //@CacheNamespace(flushInterval = 600000, size = 512, readWrite = true)
     public interface SkillDao {
            
         @Insert("INSERT INTO skill(name, level) VALUES (#{name}, #{level})")
            
         //设置id属性,方法1:
         //@SelectKey(statement = "SELECT LAST_INSERT_ID()",
         //      keyProperty = "id", before = false, resultType = Integer.class)
         //方法2
         //@Options(useGeneratedKeys = true, keyProperty = "id")
         boolean save(Skill skill);
        
         @Update("UPDATE skill SET name = #{name}, level = #{level} WHERE id = #{id}")
         boolean update(Skill skill);
        
         @Delete("DELETE FROM skill WHERE id = #{id}")
         boolean remove(Integer id);
        
         @Select("SELECT * FROM skill WHERE id = #{id}")
         Skill get(Integer id);
        
         @Select("SELECT * FROM skill")
         List<Skill> list();
            
         //设置参数名称:传参
         @Select("SELECT * FROM skill LIMIT #{start}, #{size}")
         List<Skill> listByStartAndSize(@Param("start") int start,
                                    @Param("size") int size);
                                       
         //批量保存,script标签内部可以引用xml中直接使用的标签内容
         //因为注解没有foreach语句
         @Insert("<script>" +
                     "INSERT INTO skill(name, level) VALUES" +
                     "<foreach collection=\"list\" item=\"skill\" separator=\",\">" +
                         "(#{skill.name}, #{skill.level})" +
                     "</foreach>" +
                 "</script>"
         )
         boolean batchSave(@Param("skills") List<Skill> skills);
     }
    
  4. test测试

     //其余跟方法二一样
     //新增
     @Test
     public void list2() {
         try (SqlSession session = MyBatises.openSession()) {
             // 生成SkillDao的代理对象
             SkillDao dao = session.getMapper(SkillDao.class);
             List<Skill> skills = dao.listByStartAndSize(0, 10);
             for (Skill skill : skills) {
                 System.out.println(skill);
             }
         }
     }
    

复杂数据结构的注释使用

  1. 当bean对象属性有对象类型、集合类型如何使用注释?
  2. 仍然引入多表关系使用的复杂数据:一个人有n张银行卡、一张身份证、n分职业的场景
  3. bean中添加Person、Job、IdCard、BandCard
  4. dao文件中添加对应的接口文件
    1. PersonDao文件:

       public interface PersonDao {
           @Select("SELECT * FROM person WHERE id = #{id}")
           //@Results相当于<resultMap>,@Result:对应<id>、<result>
           @Results(id = "get", value = {
               //id=ture:代表是id标签,默认false,等价于 <id property="id" column="id"/>
               @Result(property = "id", column = "id", id = true),
               @Result(property = "name", column = "name"),
               /* 身份证 */
               //column是给select传参数
               @Result(
                   property = "idCard",
                   column = "id",
                   one = @One(fetchType = FetchType.LAZY, select = "com.zh.dao.IdCardDao.getByPerson")
               ),
               /* 银行卡 */
               @Result(
                   property = "bankCards",
                   column = "id",
                   many = @Many(fetchType = FetchType.LAZY, select = "com.zh.dao.BankCardDao.listByPerson")
               ),
               /* 工作 */
               @Result(
                   property = "jobs",
                   column = "id",
                   many = @Many(fetchType = FetchType.LAZY, select = "com.zh.dao.JobDao.listByPerson")
               )
           })
           Person get(Integer id);
              
           @Select("SELECT * FROM person")
           /* 引用id为get的@Results,ResultMap代表返回数据为集合,每个元素的映射方式 */
           @ResultMap("get")
           List<Person> list();
              
           //xml与注解混合使用:查询sql在Person.xml中
           Person testGet();
              
       //    演示标签使用
       //    @insert("<script>" +
       //
       //    )
           boolean batchSave(@Param("skills") List<Skill> skills);
       }
      
    2. 其他dao文件:

       public interface BankCardDao {
           //通过id查询银行卡信息
           @Select("SELECT * FROM bank_card WHERE person_id = #{personId}")
           List<BankCard> listByPerson(Integer personId);
       }
       public interface IdCardDao {
           //通过id查询身份证信息
           @Select("SELECT * FROM id_card WHERE person_id = #{personId}")
           IdCard getByPerson(Integer personId);
       }
       public interface JobDao {
           //通过id查询工作信息
           @Select(
                   "SELECT j.* FROM job j "
                 + "JOIN person_job pj ON j.id = pj.job_id AND pj.person_id = #{personId}"
           )
           List<Job> listByPerson(Integer personId);
       }
      
  5. 混合注解、xml开发,新建一个person.xml

     <mapper namespace="com.zh.dao.PersonDao">
         <select id="testGet" resultType="Person">
             SELECT * FROM person WHERE id = 1
         </select>
     </mapper>
    
  6. 核心配置mybatis-config.xml文件

     <configuration>
         <!-- 这样写就相当于下面注释的内容-->
         <properties resource="druid.properties" />
         <!--其他设置 -->
         <settings>
             <!-- name与value的内容在mysql官方可以查询:开启驼峰命名自动映射,比如从数据库列名my_age映射到Bean属性名myAge -->
             <setting name="mapUnderscoreToCamelCase" value="true"/>
         </settings>
         <!-- 别名 -->
         <typeAliases>
             <!-- 一旦设置了别名,它是不区分大小写的 -->
             <typeAlias type="com.zh.bean.Skill" alias="skill" />
             <typeAlias type="com.zh.bean.Person" alias="Person" />
             <typeAlias type="com.zh.common.DruidDataSourceFactory" alias="druid" />
         </typeAliases>
        
         <!-- 设置使用什么环境-->
         <environments default="development">
             <!-- 开发环境(调试阶段) -->
             <environment id="development">
                 <!-- 采用JDBC的事务管理方法 -->
                 <transactionManager type="JDBC" />
                 <dataSource type="DRUID">
                     <property name="driverClass" value="${dev.driverClass}"/>
                     <property name="url" value="${dev.url}"/>
                     <property name="username" value="${dev.username}"/>
                     <property name="password" value="${dev.password}"/>
                     <property name="initialSize" value="${dev.initialSize}"/>
                     <property name="maxActive" value="${dev.maxActive}"/>
                     <property name="maxWait" value="${dev.maxWait}"/>
                 </dataSource>
             </environment>
         </environments>
         <mappers>
             <mapper class="com.zh.dao.SkillDao"/>
             <!--与<mapper resource="mappers/person.xml"/>重复导入-->
             <!--        <mapper class="com.zh.dao.PersonDao"/>-->
             <mapper class="com.zh.dao.IdCardDao"/>
             <mapper class="com.zh.dao.JobDao"/>
             <mapper class="com.zh.dao.BankCardDao"/>
             <!--xml与注解,混合使用,注意:不能重复导入:这个包含了导入PersonDao -->
             <mapper resource="mappers/person.xml"/>
             <!--可以通过这种方式,导出该包下所有的dao -->
             <!--<package name="com.zh.dao"/>-->
         </mappers>
        
     </configuration>
    

其他知识点

  1. 去除编译的时候SSL警告
    1. 在duirid.proprties文件数据库连接地址修改如下:(后面添加userSSL)

       dev.url=jdbc:mysql://localhost:3306/test-mybatis?useSSL=false
      
Table of Contents