文章目录

  • 一、今日内容
  • 二、mybatis延迟加载
    • a、什么是延迟加载
    • b、一对一延迟加载
      • 模板样式代码总结
      • 回顾昨日一对一
      • 改进一对一
      • 一对一延迟加载配置(需要时再获取 节省资源)
    • c、一对多延迟加载(也用改进的collection分离配置)
    • d、开启全局的延迟加载
  • 三、mybatis缓存
    • a、一级缓存(不需要额外的配置或代码 默认存在)
    • b.二级缓存(需要额外的修改代码与加配置 基本不用)
  • 四、mybatis注解开发(★★ 简单到怀疑人生 今后写法)
    • a、实现增删改查
      • 注解配置结果集映射
      • 主键回显(执行插入后未提交事务前获取插入记录的主键)
    • b、实现一对一
      • 结果集映射特殊情况
    • c、实现一对多
    • d、动态sql
  • mybatis 四天总结

一、今日内容

1、mybatis延迟加载
2、mybatis缓存
3、mybatis注解开发

二、mybatis延迟加载

a、什么是延迟加载

  1. 也叫懒加载
  2. 什么时候需要,什么时候去获取 什么时候需要该数据,什么时候执行sql语句去查询

b、一对一延迟加载

今日回顾一对一级联查询写法,出错点:
粗心错误:
     jdbc.properties变量名错误(模板忘记更新)
     AccountDao.xml的namespace忘记改成AccountDao的全名
     AccountDao.xml的路劲写成了cn.ahpu.domain 应该是dao包下
     AccountDao.xml的resultMap写成了resultType 映射时也有一个属性名忘记改
day04视频02很好的回顾

模板样式代码总结

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--引入外部配置文件--><properties resource="jdbc.properties"></properties><typeAliases><!--扫描整个包 包下所有类取别名为简单类名(不区分大小写)--><package name="cn.ahpu.domain"></package></typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"/><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><!--引入一个包 就引入了该包中所有接口或xml (动态代理前提也要求dao接口和xml包名一致)--><package name="cn.ahpu.dao"></package></mappers>
</configuration>

jdbc.properties

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

log4j.properties

# 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, info
# 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=d:\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

回顾昨日一对一

Account和User一对一 查询account同时得到对应唯一user

pom.xml

<?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>cn.ahpu</groupId><artifactId>mybatis_day04_1_one2one_lazy</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><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.6</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version></dependency></dependencies></project>

User.java

public class User {private Integer id;private String username;private String password;private String sex;private String address;private Date birthday;//省略get/set
}

Account.java

public class Account {private Integer id;private String name;private Float money;private Integer u_id;//一个账户对应一个用户private User user;//省略get/set
}

AccountDao.java

 // 查询所有账户 账户包含唯一的用户信息public List<Account> findAll();

AccountDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--动态代理 namesapce必须是dao接口全限定名mybatis-config.xml中配置了typeAliases 引入整个包 因而参数全限定名可以简写
-->
<mapper namespace="cn.ahpu.dao.AccountDao"><resultMap id="accounts" type="account"><id column="id" property="id"></id><result column="name" property="name"></result><result column="money" property="money"></result><result column="u_id" property="u_id"></result><!--配置唯一的user属性--><!--原来的配置--><association property="user" javaType="User"><id column="uid" property="id"></id><result column="uname" property="username"></result><result column="password" property="password"></result><result column="sex" property="sex"></result><result column="address" property="address"></result><result column="birthday" property="birthday"></result></association></resultMap><select id="findAll" resultMap="accounts">select * from account,user where u_id=uid</select></mapper>

TestMybatisOne2OneLazy.java

@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();AccountDao accountDao = sqlSession.getMapper(AccountDao.class);List<Account> list = accountDao.findAll();for (Account account : list) {System.out.println(account);}sqlSession.close();}

改进一对一

Account里只查询Account表的信息,查询User表的任务交给UserDao,Account里引入下方法全名即可
每个xml里只有本张表的映射配置了 完全隔离开了 不用怕多表查询两张表列名相同的冲突了
association 配法有变,加两个属性,具体字段便不用配置了
(Account表里u_id,可以据此来)
UserDao

 //根据id查询用户public User findById(Integer id);

UserDao.xml

<mapper namespace="cn.ahpu.dao.UserDao"><resultMap id="usero" type="user"><!--没办法 属性名和列名不一致 必须配--><id column="uid" property="id"></id><result column="uname" property="username"></result></resultMap><select id="findById" parameterType="int" resultMap="usero">select * from user where uid=#{id}</select>
</mapper>

AccountDao.xml 改进配置

<mapper namespace="cn.ahpu.dao.AccountDao"><resultMap id="accounts" type="account"><id column="id" property="id"></id><result column="name" property="name"></result><result column="money" property="money"></result><result column="u_id" property="u_id"></result><!--配置唯一的user属性-->       <!--现在改进的配置 新增配置column='外键'column="u_id":指定通过本表哪列查询对应的用户对象select:要执行的方法id 即: mapperId=namespace.id(看sql可知 user不是在此处查了)--><association property="user" javaType="user" column="u_id" select="cn.ahpu.dao.UserDao.findById"></association></resultMap><select id="findAll" resultMap="accounts">select * from account</select>
</mapper>

一对一延迟加载配置(需要时再获取 节省资源)

1.必须使用上面改进的association配置法
2.在association里配置一个fetchType=“lazy”

<mapper namespace="cn.ahpu.dao.AccountDao"><resultMap id="accounts" type="account"><id column="id" property="id"></id><result column="name" property="name"></result><result column="money" property="money"></result><result column="u_id" property="u_id"></result><!--配置唯一的user属性--><!--fetchType="lazy": lazy:延迟加载   eager:立即加载--><association property="user" javaType="user" column="u_id"select="cn.ahpu.dao.UserDao.findById" fetchType="lazy"></association></resultMap><select id="findAll" resultMap="accounts">select * from account</select>
</mapper>

TestMybatisOne2OneLazy.java
测试方法里不能直接调用toString了,因为toString访问所有属性,一定会立即加载

@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();AccountDao accountDao = sqlSession.getMapper(AccountDao.class);List<Account> list = accountDao.findAll();for (Account account : list) {System.out.println(account.getName());System.out.println(account.getUser());}sqlSession.close();}

查看日志 观察延迟加载过程

1234564156
2020-01-09 13:00:41,295 1085   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - ==>  Preparing: select * from user where uid=?
2020-01-09 13:00:41,296 1086   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - ==> Parameters: 1(Integer)
2020-01-09 13:00:41,307 1097   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - <==      Total: 1
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
242415
2020-01-09 13:00:41,311 1101   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - ==>  Preparing: select * from user where uid=?
2020-01-09 13:00:41,311 1101   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - ==> Parameters: 2(Integer)
2020-01-09 13:00:41,314 1104   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - <==      Total: 1
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
24235698
2020-01-09 13:00:41,314 1104   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - ==>  Preparing: select * from user where uid=?
2020-01-09 13:00:41,314 1104   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - ==> Parameters: 10(Integer)
2020-01-09 13:00:41,315 1105   [           main] DEBUG   cn.ahpu.dao.UserDao.findById  - <==      Total: 1
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
2020-01-09 13:00:41,315 1105   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@544a2ea6]
2020-01-09 13:00:41,316 1106   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@544a2ea6]
2020-01-09 13:00:41,316 1106   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 1414147750 to pool.

c、一对多延迟加载(也用改进的collection分离配置)

还是用户对账户一对多 一个用户对应多个账户 查询所有用户 同时查询到每个用户下的多个账户
改进的collection分离配置,同上面一对一的改进,account的查询分离出来交给accountdao自己去查,userdao里引入一下即可
Account.java 不再有User对象属性
此处一对多一方写集合 多方不写对象

public class Account {private Integer id;private String name;private Float money;private Integer u_id;
}

User.java 加上Account集合
此处一对多一方写集合 多方不写对象

public class User {private Integer id;private String username;private String password;private String sex;private String address;private Date birthday;//一个用户对应多个账户private List<Account> accounts;
}

UserDao.java

//查询所有用户:包含其下所有账户public List<User> findAll();

AccountDao.java

//根据主人用户id(u_id=uid) 查询所有账户public List<Account> findByUid(Integer uid);

AccountDao.xml

<mapper namespace="cn.ahpu.dao.AccountDao"><select id="findByUid" resultType="account" parameterType="int">select * from account where u_id=#{uid}</select>
</mapper>

UserDao.xml ★

<mapper namespace="cn.ahpu.dao.UserDao"><resultMap id="users" type="user"><id column="uid" property="id"></id><result column="uname" property="username"></result><result column="password" property="password"></result><result column="sex" property="sex"></result><result column="address" property="address"></result><result column="birthday" property="birthday"></result><!--映射 List<Account> accounts 改进写法 account的查询交给accountdao自己column:写本表的uid属性  (拿着本表uid去匹配account表的外键u_id)select:引入accountdao中对应的查询方法fetchType="lazy":延迟加载--><collection property="accounts" ofType="account" column="uid"select="cn.ahpu.dao.AccountDao.findByUid" fetchType="lazy"></collection></resultMap><select id="findAll" resultMap="users">select * from user</select>
</mapper>

TestMybatisOne2ManyLazy 测试多对多改进collection配置与延迟加载

@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);List<User> list = userDao.findAll();for (User user : list) {System.out.println(user.getUsername());//System.out.println(user.getAccounts());//不写就不会去查 account表}sqlSession.close();}

d、开启全局的延迟加载

无需一个个在collection或association中配置fetchType=“lazy”,直接开启全局懒加载

mybatis-config.xml 里加上全局配置

<!--核心配置文件全局设置--><settings><!--开启全局延迟加载--><setting name="lazyLoadingEnabled" value="true"/></settings>

注意xml里属性的顺序(,表示顺序 ?表示1个或0个)

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

配置完成后collection里的fetchType="lazy"可以删了,测试仍然有延迟加载的效果

配置完毕后默认懒加载 若某个方法需要立即加载可以仍在collection或association中配置:fetchType=“eager”
进行立即加载

三、mybatis缓存

简单理解:执行过的sql语句不会重复执行,直接到内存里拿数据

a、一级缓存(不需要额外的配置或代码 默认存在)

单表查询所有user对象为例 验证一级缓存的存在
UserDao.java

public List<User> findAll();

UserDao.xml

<mapper namespace="cn.ahpu.dao.UserDao"><resultMap id="usero" type="user"><id column="uid" property="id"></id><result column="uname" property="username"></result></resultMap><select id="findAll" resultMap="usero">select * from user</select>
</mapper>

TestMybatisCache 测试

/*** 一级缓存* 在同一sqlSession对象范围下,两次执行同一个sql语句,第二次没有执行sql语句 说明一级缓存的存在** 一级缓存执行流程*  第一次执行sql语句,查询到数据,会在一级缓存中存储sql语句和数据:以sql语句为key,数据为map存储于map中*  则第二次执行sql时,会先从缓存中查询,意sql语句为key查询,得到数据直接返回。若没有相应key,则去查数据库*/@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");//sqlSessionFactory 单例模式SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao1 = sqlSession.getMapper(UserDao.class);List<User> list1 = userDao1.findAll();for (User user : list1) {System.out.println(user);}System.out.println("=====================================================");//两次同一个sqlSessionUserDao userdao2 = sqlSession.getMapper(UserDao.class);List<User> list2 = userdao2.findAll();for (User user : list2) {System.out.println(user);}sqlSession.close();}
2020-01-09 16:15:03,599 668    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 428910174.
2020-01-09 16:15:03,599 668    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:15:03,601 670    [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==>  Preparing: select * from user
2020-01-09 16:15:03,687 756    [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==> Parameters:
2020-01-09 16:15:03,719 788    [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - <==      Total: 5
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
=====================================================
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
2020-01-09 16:15:03,723 792    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:15:03,725 794    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:15:03,725 794    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 428910174 to pool.

执行增删改与提交操作都会清空缓存

@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");//sqlSessionFactory 单例模式SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao1 = sqlSession.getMapper(UserDao.class);List<User> list1 = userDao1.findAll();for (User user : list1) {System.out.println(user);}//两次同一个sqlSession UserDao userdao2 = sqlSession.getMapper(UserDao.class);userdao2.delete(19);sqlSession.commit();//主动清空缓存//sqlSession.clearCache();System.out.println("=====================================================");//两次同一个sqlSessionUserDao userdao3 = sqlSession.getMapper(UserDao.class);List<User> list3 = userdao3.findAll();for (User user : list3) {System.out.println(user);}sqlSession.close();}
2020-01-09 16:10:24,150 1320   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:10:24,170 1340   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==>  Preparing: select * from user
2020-01-09 16:10:24,193 1363   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==> Parameters:
2020-01-09 16:10:24,236 1406   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - <==      Total: 6
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
User{id=19, username='小王', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:58 CST 2020}
2020-01-09 16:10:24,251 1421   [           main] DEBUG     cn.ahpu.dao.UserDao.delete  - ==>  Preparing: delete from user where uid=?
2020-01-09 16:10:24,251 1421   [           main] DEBUG     cn.ahpu.dao.UserDao.delete  - ==> Parameters: 19(Integer)
2020-01-09 16:10:24,271 1441   [           main] DEBUG     cn.ahpu.dao.UserDao.delete  - <==    Updates: 1
2020-01-09 16:10:24,271 1441   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
=====================================================
2020-01-09 16:10:24,279 1449   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==>  Preparing: select * from user
2020-01-09 16:10:24,279 1449   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==> Parameters:
2020-01-09 16:10:24,285 1455   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - <==      Total: 5
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
2020-01-09 16:10:24,285 1455   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:10:24,286 1456   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:10:24,286 1456   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 428910174 to pool.

主动清空缓存:

  sqlSession.clearCache();

一级缓存
  1.在同一sqlSession对象范围下,两次执行同一个sql语句,第二次没有执行sql语句 说明一级缓存的存在
  2.执行增删改,提交操作(sqlSession.commit) 会清空缓存
  3.sqlSession.clearCache(); 清空缓存
  4.一级缓存是session级别的 两个sqlSession之间是没有一级缓存可言的(因而close是更换session,并不是简单清空缓存)
一级缓存执行流程
  第一次执行sql语句,查询到数据,会在一级缓存中存储sql语句和数据:以sql语句为key,数据为map存储于map中
  则第二次执行sql时,会先从缓存中查询,意sql语句为key查询,得到数据直接返回。若没有相应key,则去查数据库

b.二级缓存(需要额外的修改代码与加配置 基本不用)

二级缓存
  1.是sessionFactory级别的 工厂只有一个 也即:它可以在多个sqlSession对象之间共享缓存数据
  2.默认是开启的
  3.在需要使用二级缓存的配置映射文件中开启
  4.需要在二级缓存中保存的pojo对象必须实现序列化接口: User implements Serializable (说明二级缓存是写到外存的)
  5.在同一个namespace范围下,执行提交操作,会清空该namespace的缓存(坑)
   eg:多表关联查询,第一次查询A关联得到B,然后B执行修改清空了缓存(或者说B的数据变了),A一直执行查询
    A一直没变,那么namespaceA.sqlAB还在缓存中,B却早已变了,查询便出错了 !巨大的坑!知道坑 用时小心!
   好在开发中二级缓存一般不用,都是自己手写

工作流程
  1.在任意一个sqlSession对象中执行了查询语句,当关闭了sqlSession对象时(说明必须关闭才有缓存),在二级缓存中保存数据
    保存数据:以namespace.sql语句为key值,以对象为value进行存储
  2.当其他sqlSession对象执行sql时,根据namespace.sql语句查询缓存,命中直接取数据,否则才去读数据库

仍然以查询所有用户为例:
相对于上面的修改:
UserDao.xml

User.java

TestMybatisCache2 测试二级缓存的存在

/*** 二级缓存*  1.是sessionFactory级别的  工厂只有一个 也即:它可以在多个sqlSession对象之间共享缓存数据*  2.默认是开启的*  3.在需要使用二级缓存的配置映射文件中开启  <cache/>*  4.需要在二级缓存中保存的pojo对象必须实现序列化接口: User implements Serializable** 二级缓存的工作流程*  1.在任意一个sqlSession对象中执行了查询语句,当关闭了sqlSession对象时(说明必须关闭才有缓存),在二级缓存中保存数据*    保存数据:以namespace.sql语句为key值,以对象为value进行存储*  2.当其他sqlSession对象执行sql时,根据namespace.sql语句查询缓存,命中直接取数据,否则才去读数据库*/@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");//sqlSessionFactory 单例模式SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao1 = sqlSession.getMapper(UserDao.class);List<User> list1 = userDao1.findAll();for (User user : list1) {System.out.println(user);}sqlSession.close();System.out.println("=====================================================");//两次不同sqlSessionSqlSession sqlSession2 = sqlSessionFactory.openSession();UserDao userdao2 = sqlSession2.getMapper(UserDao.class);List<User> list2 = userdao2.findAll();for (User user : list2) {System.out.println(user);}sqlSession.close();}

两个session,一次select语句,还计算出了二级缓存命中率

2020-01-09 16:41:13,880 1788   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67b467e9]
2020-01-09 16:41:13,882 1790   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==>  Preparing: select * from user
2020-01-09 16:41:13,916 1824   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - ==> Parameters:
2020-01-09 16:41:13,972 1880   [           main] DEBUG    cn.ahpu.dao.UserDao.findAll  - <==      Total: 5
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
2020-01-09 16:41:14,042 1950   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67b467e9]
2020-01-09 16:41:14,043 1951   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@67b467e9]
2020-01-09 16:41:14,043 1951   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 1739876329 to pool.
=====================================================
2020-01-09 16:41:14,112 2020   [           main] DEBUG            cn.ahpu.dao.UserDao  - Cache Hit Ratio [cn.ahpu.dao.UserDao]: 0.5(缓存命中率0.5)
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸾峰', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}

四、mybatis注解开发(★★ 简单到怀疑人生 今后写法)

a、实现增删改查

连xxxDao.xml都没有了,只有UserDao接口+注解和测试类了
没有dao对应的配置文件了‘’

根目录下3个配置文件和User.java 和之前一样固定不变
只用写UserDao接口和TestMtbatisCRUD测试类

mybatis-config.xml 核心配置文件中的取别名也可以删了:

UserDao.java

package cn.ahpu.dao;import cn.ahpu.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;import java.util.List;/*** @author 寒面银枪* @create 2020-01-09 17:18*/
public interface UserDao {/*** 查询所有用户* @return*/@Select("select * from user")public List<User> findAll();/*** 根据id查询* @return*/@Select("select * from user where uid=#{id}")public User findById(Integer id);/*** 根据姓名模糊查询* @param username* @return*///此处写的sql语句内只能是双引号 不能写单引号@Select("select * from user where uname like \"%\"#{username}\"%\"")public List<User> findByUsername(String username);/*** 查询总记录数* @return*/@Select("select count(*) from user")public Integer findTotalCount();/*** 添加用户* @param user*/@Insert("insert into user values(null,#{username},#{password},#{sex},#{address},#{birthday})")public void insert(User user);/*** 更新用户* @param user*/@Update("update user set uname=#{username},password=#{password},sex=#{sex}," +"address=#{address},birthday=#{birthday} where uid=#{id}")public void update(User user);/*** 根据id删除用户* @param id*/@Delete("delete from user where uid=#{id}")public void delete(Integer id);}

TestMtbatisCRUD

package cn.ahpu;import cn.ahpu.dao.UserDao;
import cn.ahpu.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;import java.io.InputStream;
import java.util.Date;
import java.util.List;/*** @author 寒面银枪* @create 2020-01-09 18:19*/
public class TestMtbatisCRUD {@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);List<User> list = userDao.findAll();for (User user : list) {System.out.println(user);}sqlSession.close();}@Testpublic void testFindById(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);User user = userDao.findById(18);System.out.println(user);sqlSession.close();}@Testpublic void testFindByUsername(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);List<User> list = userDao.findByUsername("s");for (User user : list) {System.out.println(user);}sqlSession.close();}@Testpublic void testFindTotalCount(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);Integer totalCount = userDao.findTotalCount();System.out.println(totalCount);sqlSession.close();}@Testpublic void testInsert(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);User user = new User();user.setUsername("mayun");user.setAddress("杭州");user.setSex("男");user.setPassword("123");user.setBirthday(new Date());userDao.insert(user);sqlSession.commit();sqlSession.close();}
}

注解配置结果集映射

表列名与类属性名不同,即结果集映射,也用注解完成

/*** 查询所有用户* @return** @Results:映射结果集* @Result:映射表列名和属性名不一样的列*      id=true:表示id是主键* 注解中只需要写:列名与属性名不一致的即可 (不像xml中那样有时需要全写)(唯一特殊见四、b、结果集映射特殊情况 用超过1次就必须配置)*/@Select("select * from user")@Results({@Result(id = true, column = "uid", property = "id"),@Result(column = "uname", property = "username")})public List<User> findAll();

主键回显(执行插入后未提交事务前获取插入记录的主键)

mysql中获取主键无意义 oracle中有作用

注解方式:

/*** 添加用户* @param user*///保存后获取主键  (执行insert代码后就可以获取主键)//keyProperty:主键属性名  keyColumn:主键列名  resultType:主键类型// before=false:非添加之前查询,也即是添加之后可以查询主键// before=true:添加之前可查询主键 (可惜mysql不支持 oracle支持)//statement:需要执行的sql语句//select last_insert_id():最后一次执行添加生成的主键 (也即是要查询的主键)@SelectKey(keyProperty = "id",keyColumn = "uid",resultType = Integer.class,before = false,statement = "select last_insert_id()")@Insert("insert into user values(null,#{username},#{password},#{sex},#{address},#{birthday})")public void insert(User user);

xml方式

<insert id="save" parameterType="user"><selectKey keyColumn="uid" keyProperty="id" resultType="int" order="AFTER">select last_insert_id()</selectKey>insert into user values(null,#{username},#{password},#{sex},#{address},#{birthday})
</insert>

TestMtbatisCRUD 测试代码:

@Testpublic void testInsert(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);User user = new User();user.setUsername("mayun");user.setAddress("杭州");user.setSex("男");user.setPassword("123");user.setBirthday(new Date());userDao.insert(user);//不加注解此处获取不了主键  (为何commit之前 注意3大框架整合后不能轻易提交 一提交就不能再用session 万一延迟加载 有些数据就永远丢了)System.out.println(user.getId());sqlSession.commit();sqlSession.close();}

b、实现一对一

账户与用户一对一 查询账户同时查询其唯一对应的用户
domain:
User.java

public class User {private Integer id;private String username;private String password;private String sex;private String address;private Date birthday;
}

Account.java

public class Account {private Integer id;private String name;private Float money;private Integer u_id;//一个账户对应一个用户private User user;
}

UserDao.java

public interface UserDao {//一对一 account那里传过来的就是user主键@Select("select * from user where uid=#{id}")//可怜表列名还是与pojo属性名不一致@Results({@Result(id=true,property = "id",column = "uid"),@Result(property = "username",column = "uname")})public User findById(Integer id);
}

AccountDao.java ★ 注解

public interface AccountDao {/*** 查询所有账户 包含对应唯一用户* @return*/@Select("select * from account")//属性与列不一致就得写结果映射 user肯定没有匹配列 自然要写结果映射//one=@One(select = "",fetchType="") 属性是单个对应//many:属性是多个对应(一对多中的一方写这个)//fetchType = FetchType.LAZY:提取方式为延迟加载 注意位置 (也可以直接在mybatis-config.xml中配置全局的)@Results({@Result(property = "user",column = "u_id",javaType = User.class,one=@One(select = "cn.ahpu.dao.UserDao.findById",fetchType = FetchType.LAZY))})public List<Account> findAll();
}

TestMybatisAnnoOne2One 测试类

public class TestMybatisAnnoOne2One {@Testpublic void testFindAll(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();AccountDao accountDao = sqlSession.getMapper(AccountDao.class);List<Account> list = accountDao.findAll();for (Account account : list) {System.out.println(account.getName()+"|"+account.getMoney()+"元");System.out.println(account.getUser());}sqlSession.close();}
}

结果集映射特殊情况

之前说过,注解开发只用写列名和属性名不同的映射,其他不用写,但若一个列被引用了两次或两次以上,则两次映射都必须写出来
eg:上面的一对多关联查询,外键u_id作为查询user的参数引用了一次,那么再在测试代码中打印输出account.u_id属性值就为null了,除非再加个配置 (u_id可以传递参数了 不能轻易删除 不过java代码内确实用不到它)

测试代码输入u_id

1.AccountDao.java没有单独配置u_id映射

2.AccountDao.java中配置u_id映射

c、实现一对多

一个用户对应多个账户 查询用户同时要包含其下所有的账户
Account.java

public class Account {private Integer id;private String name;private Float money;private Integer u_id;

User.java

public class User {private Integer id;private String username;private String password;private String sex;private String address;private Date birthday;private List<Account> accounts;
}

AccountDao.java

public interface AccountDao {/*** 根据u_id查询账户* @param uid* @return* #{}此处也可以随便写*/@Select("select * from account where u_id=#{uid}")public List<Account> findByUid(Integer uid);
}

UserDao.java ★

1.List属性的类型可以省略不配 要配也只能配javaType = List.class, 此处,没有ofType
2.延迟加载同样也可以直接配置一个全局的

public interface UserDao {/*** 查询所有用户 包含每个用户下的所有多个账户* @return*/@Select("select * from user")@Results({@Result(id=true,property = "id",column = "uid"),@Result(property = "username",column = "uname"),//特殊两处: 1.xml内写ofType 此处只有javaType就只能配javaType了//          2.javaType不能list中单个元素的类型 直接写集合类型List.class//但其实此处可以省略不配(2个特殊点就都不用记忆了)  下面的javaType = List.class,可以直接删掉 ^_^@Result(property = "accounts",column = "uid",javaType = List.class,many = @Many(select = "cn.ahpu.dao.AccountDao.findByUid",fetchType = FetchType.LAZY))})public List<User> findAll();
}

d、动态sql

单表查询user 姓名模糊查询 性别等于查询

动态sql语句太复杂,不可能直接在注解上写if for等逻辑了,mybatis的处理是提供新标签:
@SelectProvider(type=UserSqlProvider.class, method=“findByCondition”)
也即是传入一个类的方法,返回值为String字符串,也就是让你自己在java代码逻辑内拼接好sql传回来
好处:多复杂的sql语句都不用怕了,java字符串拼接对程序员来说太简单了

User.java

public class User {private Integer id;private String username;private String password;private String sex;private String address;private Date birthday;
}

UserDao.java 注解★

public interface UserDao {/*** 根据姓名模糊查询,性别等于查询* @return*///@Select("select * from user where uname like \"%\"#{username}\"%\" and sex=#{sex}")//一个字符串内写if for语句不大现实 换用新的标签// SelectProvider: sql语句提供者//  属性; type:sql语句提供者的类字节码(.class)//         method:提供类中的方法@SelectProvider(type=UserSqlProvider.class, method="findByCondition")@Results({@Result(id = true, column = "uid", property = "id"),@Result(column = "uname", property = "username")})public List<User> findByCondition(User user);
}

UserSqlProvider.java ☆

public class UserSqlProvider {//方法名和提供对象取得一致吧 好对应    参数user是dao中方法的参数 直接写 会自动传进来的public String findByCondition(User user){//经常修改用String不合适 要想线程安全也必须用StringBufferStringBuffer sb=new StringBuffer();sb.append("select * from user where 1=1 ");//注意空格if(user.getUsername()!=null){sb.append(" and uname like \"%\"#{username}\"%\" ");}if(user.getSex()!=null){sb.append(" and sex=#{sex} ");}return sb.toString();//在这里拼接sql无论多复杂都能做好了 java代码字符串拼接太熟悉了//唯一注意拼接时的空格 最好前后都多加空格}
}

TestMtbatisCRUD 测试方法类

@Testpublic void testFindByCondition(){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserDao userDao = sqlSession.getMapper(UserDao.class);User user = new User();//user.setUsername("s");user.setSex("男");List<User> list = userDao.findByCondition(user);for (User u : list) {System.out.println(u);}sqlSession.close();}

mybatis 四天总结

1. mybatis 第一天 自定义框架mybaits 的入门
2. mybatis 第二天CRUD : selectList ,selectOne ,insert ,update ,delete#{ } ${ }参数类型:简单类型: ${ value} #{随便}pojo类型:${属性名} #{属性名}复杂类型:map: #{key}多参数:#{param1} , #{param2}....传统模式开发: UserDaoImpl implements UserDao动态代理开发:UserDao核心配置文件:properties , typeAliaes , mappers ,settings(延迟加载,二级缓存, 日志)返回值类型:
3. mybatis第三天数据源:type=POOLED(自带的以后不会用) --- c3p0 ,dbcp ,spring jdbc, druid(阿里出品数据源)事务:openSession(true | false)动态的sql语句:if , where , sql片段 , foreach一对一映射:一对多映射:多对多映射:
4. mybatis第四天延迟加载(以后都用) -- 按需加载一级缓存: session级别的,二级缓存:sessionFactory,应用级别的(漏洞 基本不用)注解开发: (★)

mybatis-day04-改进多表查询collection配置、延迟加载、缓存、注解开发(★)相关推荐

  1. mybatis 多表关联查询_Java修行第041天--MyBatis框架(下)--多表查询

    1 解决列名和属性名不一致问题 如果查询时使用 resultType 属性, 表示采用 MyBatis 的Auto-Mapping(自动映射)机制, 即相同的列名和属性名会自动匹配. 因此, 当数据库 ...

  2. MyBatis如何实现多表查询

    MyBatis如何实现多表查询 先说结论,在MyBatis中我们有两种方式来实现多表查询 一.使用sqlmapper配置文件 二.使用注解 直入主题首先我们来看如何使用配置文件的方式来实现一对一,和一 ...

  3. MyBatis动态SQL_多表查询_延迟加载_缓存

    POJO包装传递对象 //POJO实体 public class QueryConditionObject {private User user;//user get,set方法 }//Dao 接口 ...

  4. mybatis中的多表查询

    首先我们先创建两个表和pojo用来演示多表查询,如下: 用户表:t_emp表,其中emp_id为自增主键. t_emp对应的pojo:Employee public class Employee {p ...

  5. MyBatis如何实现多表查询(一对一、一对多)

    Mybatis实现多表的查询分为以下两种: 方式一:sqlMapper配置文件 一对一:在resultMap标签中使用 association 标签 一对多:在resultMap 标签中使用colle ...

  6. mybatis 高级映射 - 一对多查询 - collection

    方法一 连接表查询 案例:查询所有订单信息及订单下的订单明细信息. 订单信息与订单明细为一对多关系. 使用resultMap实现如下: Sql语句: SELECT orders.*,user.user ...

  7. mycat的主从关系 垂直分库 水平分表 以及mycat分片联表查询的配置详解(mysql5.7系列)

    主从关系 准备三台不同ip的虚拟机 (第一批)主从关系的配置 主192.168.47.131 配置/etc/my.cnf,在[mysqld]下配置 log-error=/var/log/mysqld. ...

  8. 取代 Mybatis Generator,这款代码生成神器配置更简单,开发效率更高

    作为一名 Java 后端开发,日常工作中免不了要生成数据库表对应的持久化对象 PO,操作数据库的接口 DAO,以及 CRUD 的 XML,也就是 mapper. Mybatis Generator 是 ...

  9. Mybatis整体学习笔记-CRUD-配置解析-结果集映射-日志-注解开发-复杂环境-动态SQL-缓存

    MyBatis 要多对官方文档进行学习 https://mybatis.org/mybatis-3/zh/index.html 简介 MyBatis 持久层框架 Dao Access Objects ...

最新文章

  1. LNMP(Nginx负载均衡,SSL原理,Nginx配置SSL,生产SSL密钥对)
  2. DPDK网络处理模块划分
  3. [Android开发]zip文件压缩解压缩
  4. linux文件偏移函数实验报告,Linux实验报告二.doc
  5. iso 绝对pe_深度 WinPE 4.2 维护光盘ISO(含U盘PE制作工具) 下载地址
  6. 如何说服老板页面兼容IE9+
  7. 南京信息工程大学改立元宇宙工程系,网友:我很好奇课程是啥
  8. 2022年最新云开发去水印小程序源码
  9. 冷门项目玩法思路,小竞争大利润
  10. 年末放大招,Java进阶大数据3W全套视频免费领!
  11. 最大化参数 火车头_初级火车头采集器教程分享
  12. 优盘格式化工具,U盘修复格式化卡死问题
  13. wince蓝屏_Windows7更新补丁蓝屏错误代码6B的暂时解决方法
  14. 3DMAX快捷键整理
  15. ISO13485医疗器械质量管理体系认证意义
  16. 字节一面:Redis主节点宕机,如何处理?
  17. MyBatis Plus简介和入门案例
  18. java 使用抽象类解决长方形与圆求周长与面积的实现
  19. 克隆数据库linux 不能登录,DockerSQL2017Linux容器的数据库克隆
  20. 如何打开windows的服务services.msc

热门文章

  1. ui-app使用pdfh5显示pdf文件 获取pdf总页数和当前阅读页数
  2. War3Tool dota改键v3.3版
  3. MySQL unique啥意思_数据库中unique是什么意思
  4. 简述企业信息系统持续开发与持续集成相关主流技术(DI/CI)
  5. Java和开源GIS平台
  6. android 获取经纬度 有负值吗,获取纬度和经度的负值
  7. sql inject1
  8. mac如何卸载python2.7_mac python环境的安装与卸载
  9. 【android】SSL peer shut down incorrectly
  10. Layui表格获取行数据