Mybatis-02-日志,注解和分页,多对一,一对多,缓存

Mybatis官方文档:https://mybatis.org/mybatis-3/zh/index.html
Mybatis基础配置详解:Mybatis-01-配置详解

四:Mybatis日志和分页

  1. 什么是log4j
  • log4j是apache的一个开源项目,通过使用它我们可以控制日志信息输送的目的地是控制台,文件,或GUI组件,也可以控制每一条日志的输出格式。
  • 通过定义每一条日志信息的级别,我们能够更加细致的控制日志的生成过程。通过一个配置文件来灵活的进行配置,而不需要修改应用的代码。
  1. 如何使用?
  • 导入log4j依赖
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.0</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  • 资源路径下新建log4j.properties配置文件,在文件中进行具体配置
### 设置日志信息 ###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到./log
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ./log/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =./log/log.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
  • 在mybatis的xml文件中配置setting实现,引入log4j。注:Mybatis的xml文件中,各种标签具有严格的排序顺序,在编写过程中需要注意标签顺序。
<settings>
    <!--配置日志工厂,日志为STDOUT_LOGGING(Mybatis自带),还有其他日志工厂可供配置-->
    <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <setting name="logImpl" value="Log4j"/>
</settings>
  • 在类对象中创建Logging对象,进行添加信息即可static Logger logger = Logger.getLogger(UserMapperTest.class);logger.info(“info”")
static  Logger logger = Logger.getLogger(UserMapperTest.class);
/*日志测试*/
@Test
public void testUserMapperTest(){
    logger.debug("dubug:进入测试");
    logger.info("info:进入测试");
    logger.error("error:进入测试");
}
  1. Mybatis分页
  • mybatis分页有多种方法,第一种为limit关键字分页,传入示例如下
<select id="getUserLimit" resultMap="user" parameterType="map">
   select * from user limit #{startIndex},#{pageSize}
</select>
//接口代码
List<User> getUserLimit(Map<String,Object> map);
@Test
public void testLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("startIndex",1);
    map.put("pageSize",2);
    List<User> users = mapper.getUserLimit(map);
    for (User user : users) {
        System.out.println(user);
    }

    sqlSession.close();
}
  • 第二种是使用RowBounds插件
//mapper接口定义
//使用RowBounds实现分页
List<User> getUserByRowBounds();
<!--通过RowBounds实现分页-->
<select id="getUserByRowBounds" resultMap="user">
    select * from user
</select>
/*RowBounds分页测试*/
@Test
public void testRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    RowBounds rowBounds = new RowBounds(1,2);
    List<User> users = sqlSession.selectList("com.chelsea.mapper.UserMapper.getUserByRowBounds",null,rowBounds);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

五:Mybatis注解

  1. sql简单的时候可以使用注解开发,简化项目操作
  2. 使用注解开发时,需要在mybatis-config.xml文件下配置注解开发类
<!--使用注解开发时候,也需要在mappers中进行注册,使用类进行注册-->
<!--对比之前的xml文件形式注册如下-->
<mappers>
	<mapper class="com.chelsea.mapper.UserMapper"/>
	<mapper resource="com/chelsea/dao/UserMapper.xml"/>
</mappers>
  1. 在定义的mapper接口中,对于每个方法在方法上使用select等接口进行操作实现,但对于一些复杂sql会显得力不从心,形式如下:
  • 使用注解进行开发时候,对于属性名,返回值等需要严格按照约定进行,否则会有空值等信息
public interface UserMapper {

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

    /*删除用户*/
    @Delete("delete from user where id = #{id}")
    void deleteById(@Param("id") int id);

    /*添加用户*/
    @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
    void addUser(User user);

    /*更新用户信息*/
    @Update("update user set name=#{name},pwd=#{password} where id=#{id}")
    void updateUser(User user);

}

六:lombok的使用—偷懒专用

  1. 在ide插件设置中,安装Lombok插件(搜索安装即可)
    Mybatis-02-日志,注解和分页,多对一,一对多,缓存
  2. 在Dependence中引入lombok依赖
<!--偷懒专用,可以省去写getset等方法,前提是ide必须安装lombok插件-->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>
  1. 在pojo bean中使用注解,常用注解如下:@Data,@AllArgsConstructor, @NoArgsConstructor,@ToString等

七:Mybatis中的一对多和多对一关系

  1. 关联-association 【多对一】:多个学生对应一个老师,查询结果为同个老师下的所有学生。
  • 解决方案一:设置子查询
<resultMap id="studentAss" type="Student">
        <!--基本类型对应直接用result即可-->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--引用类型用association-->
        <!--property和column和之前一样,javaType是它的类型,因为它是一个对象。查出来的id会自动传入select-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<select id="getAllStudent" resultMap="studentAss">
    select * from student
</select>

<select id="getTeacher" resultType="Teacher">
    select * from teacher where id=#{id}
</select>
  • 解决方案二:按照结果进行嵌套查询
<!--第二种方式:按照结果嵌套查询-->
<select id="getAllStudent" resultMap="studentAss">
    select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid = t.id
</select>

<resultMap id="studentAss" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>
  1. 集合-collection 【一对多】 查询结果为一个老师,有个集合类型的学生存放所有学生。
  • 解决方案一:连表查询
<select id="getTeacherInfo" resultType="Teacher">
    select * from teacher
</select>

<!--第一种方式:多对一:联表查询-->
<select id="getTeacher" resultMap="studentOfIdTeacher">
    select s.id sid,s.name sname,t.name tname,t.id tid from student s,teacher t where s.tid = t.id and t.id = #{tid}
</select>

<resultMap id="studentOfIdTeacher" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--复杂的对象需要单独处理,对象:association 集合:collection
        javaType=“”指定属性的类型!
        集合中的泛型信息,使用ofType获取
    -->
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>
  • 解决方案二:子查询方案
<!--第二种方式:多对一:子查询(套路,注册两个,查询一个在查询)-->
<select id="getTeacher" resultMap="studentOfIdTeacher">
    select * from teacher where id = #{tid}
</select>

<resultMap id="studentOfIdTeacher" type="Teacher">
    <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>

<select id="getStudentByTeacherId" resultType="Student">
    select * from student where tid = #{tid}
</select>
<!--子查询方式经常会出现id=0,个人推荐级联查询-->
  • javaType:用来指定实体类中属性的类型
  • ofType:用来指定集合泛型中的约束类型
  • 注意:保证SQL的可读性,尽量保证通俗易懂。注意一对多和多对一中,属性名和字段的问题。如果问题不好排查错误,可以使用日志,建议使用log4j

八:Mybatis缓存

  1. mybatis的缓存分为一级缓存和二级缓存,sqlSession级别的为一级缓存也叫本地缓存,同义词会话期间查询到的数据会放到本地缓存中,以后如果需要获取相同数据,直接从缓存中拿,不必要查询数据库。如下测试类输出为True:
@Test
public void queryTest(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    User user = sqlSession.getMapper(UserMapper.class).getUserById(4);
    System.out.println(user);
    User user1 = sqlSession.getMapper(UserMapper.class).getUserById(4);
    System.out.println(user1);
    /*两次查询都是一个sqlSession中,所以第一次查询后会放入缓存,所以两次是一个对象,只执行了一次sql*/
    System.out.println(user==user1);
    sqlSession.close();
}
  1. 一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接(每个用户是一个SqlSession,所以用处不太大)
  2. 缓存失效的情况
  • 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
  • 查询不同的东西,肯定缓存也会失效(就是user1!=user2)
  • 查询不同的Mapper.xml文件,缓存会失效
  • 手动清理缓存
  1. 二级缓存开启:只需要在SQL映射文件中添加一行 ,原因是因为一级缓存作用域太低了,二级缓存是基于一个namespace级别的缓存。
  • 工作机制:一个会话查询一条数据,这个数据会被放到当前会话的一级缓存中,但如果这个会话关闭了,对应的一级缓存就不存在了。我们需要会话关闭后一级缓存中的数据会被保存到二级缓存中,新的会话就可以从二级缓存中获取内容。而不同mapper查出的数据会放到自己对应的缓存中(map),二级缓存开启步骤如下:
  • 开启全局缓存:(默认值其实本来就是True)
<setting name="cacheEnabled" value="true"/>
  • 在要使用的二级缓存的Mapper中开启,eviction为缓存策略,flushInterval为每隔60秒书信,最多可存储结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此对他们进行修改可能会在不同线程中的调用中产生冲突:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>  
  • 测试
  1. 只要开启了二级缓存,在同一个Mapper下就有效,所有的数据就会先放在一级缓存中。默认放在一级缓存,只有当会话提交或关闭的时候,才会提交到二级缓存。但从用户角度看,会先在二级缓存中查找,在查找一级缓存,都没有的话查询数据库 。(二级缓存离得近)
  2. 缓存原理图如下
    Mybatis-02-日志,注解和分页,多对一,一对多,缓存
上一篇:log4j.properties配置,Mybati-config.xml配置,Mapper配置


下一篇:log4j.properties配置文件写法