Mybatis官方文档:https://mybatis.org/mybatis-3/zh/index.html
Mybatis基础配置详解:Mybatis-01-配置详解
四:Mybatis日志和分页
- 什么是log4j
- log4j是apache的一个开源项目,通过使用它我们可以控制日志信息输送的目的地是控制台,文件,或GUI组件,也可以控制每一条日志的输出格式。
- 通过定义每一条日志信息的级别,我们能够更加细致的控制日志的生成过程。通过一个配置文件来灵活的进行配置,而不需要修改应用的代码。
- 如何使用?
- 导入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:进入测试");
}
- 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注解
- sql简单的时候可以使用注解开发,简化项目操作
- 使用注解开发时,需要在mybatis-config.xml文件下配置注解开发类
<!--使用注解开发时候,也需要在mappers中进行注册,使用类进行注册-->
<!--对比之前的xml文件形式注册如下-->
<mappers>
<mapper class="com.chelsea.mapper.UserMapper"/>
<mapper resource="com/chelsea/dao/UserMapper.xml"/>
</mappers>
- 在定义的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的使用—偷懒专用
- 在ide插件设置中,安装Lombok插件(搜索安装即可)
- 在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>
- 在pojo bean中使用注解,常用注解如下:@Data,@AllArgsConstructor, @NoArgsConstructor,@ToString等
七:Mybatis中的一对多和多对一关系
- 关联-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>
- 集合-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缓存
- 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();
}
- 一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接(每个用户是一个SqlSession,所以用处不太大)
- 缓存失效的情况
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的东西,肯定缓存也会失效(就是user1!=user2)
- 查询不同的Mapper.xml文件,缓存会失效
- 手动清理缓存
- 二级缓存开启:只需要在SQL映射文件中添加一行 ,原因是因为一级缓存作用域太低了,二级缓存是基于一个namespace级别的缓存。
- 工作机制:一个会话查询一条数据,这个数据会被放到当前会话的一级缓存中,但如果这个会话关闭了,对应的一级缓存就不存在了。我们需要会话关闭后一级缓存中的数据会被保存到二级缓存中,新的会话就可以从二级缓存中获取内容。而不同mapper查出的数据会放到自己对应的缓存中(map),二级缓存开启步骤如下:
- 开启全局缓存:(默认值其实本来就是True)
<setting name="cacheEnabled" value="true"/>
- 在要使用的二级缓存的Mapper中开启,eviction为缓存策略,flushInterval为每隔60秒书信,最多可存储结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此对他们进行修改可能会在不同线程中的调用中产生冲突:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
- 测试
- 只要开启了二级缓存,在同一个Mapper下就有效,所有的数据就会先放在一级缓存中。默认放在一级缓存,只有当会话提交或关闭的时候,才会提交到二级缓存。但从用户角度看,会先在二级缓存中查找,在查找一级缓存,都没有的话查询数据库 。(二级缓存离得近)
- 缓存原理图如下