嵌套查询
为了解决查询时候带来的问题
#多表查询
SELECT * FROM tbl_user tu
LEFT JOIN tbl_account ta ON ta.uid=tu.id
#内连接 外连接 子查询 : 嵌套查询其实跟子查询差不多
#得到用户信息
SELECT * FROM tbl_user;
#还希望得到账户信息
SELECT * FROM tbl_account WHERE uid = 2;
#嵌套查询其实就是将sql语句分开成两条sql语句执行即可
一对一嵌套查询
查询账户 将与之相关的用户信息查询
环境搭建
接口
映射文件
AccountDao.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="com.llz.dao.AccountDao">
<!--
<association 一对一配置 property=""></association>
property="" 实体类中的属性名称
javaType="" 全限定类名 只不过我们此处用的是别名
select="" 表示 调用哪段配置 定位到 我们需要执行的sql位置 namespace+id
column="" 数据库的列名 此处指的其实就是外键
-->
<!--对账户信息的配置描述-->
<resultMap id="baseAccountUser" type="account">
<!--主键-->
<id column="id" property="id"></id>
<!--普通配置-->
<result column="money" property="money"></result>
<result column="address" property="address"></result>
<result column="aname" property="aname"></result>
<result column="uid" property="uid"></result>
<!--一对一:外键 根据uid 查询用户的信息-->
<association property="user" javaType="user"
select="com.llz.dao.UserDao.findByUid" column="uid"></association>
</resultMap>
<!--
//查询所有的账户信息
public List<Account> findAllAccount();
resultType="account" 自动封装 -> 手动封装
-->
<select id="findAllAccount" resultMap="baseAccountUser" >
select * from tbl_account
</select>
</mapper>
UserDao.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="com.llz.dao.UserDao">
<!--
//根据uid 查询用户的信息
public User findByUid(Integer uid);
-->
<select id="findByUid" parameterType="int" resultType="user">
select * from tbl_user where id = #{id}
<lect>
</mapper>
测试类:
public class TestDemo {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before //在执行测试类方法之前执行
public void before(){
//获得工厂对象
sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();
//获得sqlSession
sqlSession = sqlSessionFactory.openSession();
}
/**
* 查询所有的账户信息
* 查询出账户信息的同时 希望得到每一个账户对应的用户信息
* 1.查询出账户信息
* 一对一:在账户中进行特殊配置 表示 查询账户的时候 同时查询用户信息
*/
@Test
public void testFindAllAccount(){
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<Account> accountList = accountDao.findAllAccount();
for (Account account : accountList) {
System.out.println(account);
//得到uid 如果不使用嵌套查询,就必须先查account表再查user表,效率较低
//当我们使用association 做了一对一嵌套查询后边的代码可注释掉
// Integer uid = account.getUid();
// User user = userDao.findByUid(uid);
//account.setUser( user );
}
}
@After //在执行测试类方法之后执行
public void after(){
MyBatisUtils.commitAndClose(sqlSession);
}
}
一对多嵌套查询
查询用户 将与之相关的账户信息查询
环境搭建
接口
映射文件
UserDao.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="com.llz.dao.UserDao">
<!--
type="user" v表示当前sql语句执行的最后返回结果
<collection property=""></collection> 一对多配置
property="" 封装到实体类中的属性名称
javaType="" 集合的类型
ofType=""集合中的泛型类型 全限定类名
select="" 执行另外一条sql语句 namespace+id
column="" 传递的参数
-->
<resultMap id="baseUseAccount" type="user">
<!--主键-->
<id column="id" property="id"></id>
<!--其他字段配置-->
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<result column="telephone" property="telephone"></result>
<!--配置外键 查询另一个表的数据即可-->
<collection property="accounts" javaType="list" ofType="account"
select="com.llz.dao.AccountDao.findById" column="id"></collection>
</resultMap>
<!--
//查询所有的用户信息
public List<User> findAll();
-->
<select id="findAll" resultMap="baseUseAccount">
select * from tbl_user
</select>
</mapper>
AccountDao.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="com.llz.dao.AccountDao">
<!--
//根据用户的id 查询用户下对应的账户信息
public List<Account> findById(Integer uid);
-->
<select id="findById" parameterType="int" resultType="account">
select * from tbl_account where uid = #{uid}
</select>
</mapper>
测试类:
public class TestDemo {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before //在执行测试类方法之前执行
public void before(){
//获得工厂对象
sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();
//获得sqlSession
sqlSession = sqlSessionFactory.openSession();
}
/**
* 案例效果:一对多: 查询用户信息 同时 得到用户下所有的账户信息
* 1.查询用户信息\
* 一对多的目标查询的是用户信息
*/
@Test
public void testFindAllUser(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.findAll();
for (User user : userList) {
System.out.println(user);
//List<Account> list = accountDao.findByUid(user.getId());
//user.setAccounts(list);
}
}
@After //在执行测试类方法之后执行
public void after(){
MyBatisUtils.commitAndClose(sqlSession);
}
}
加载策略-延迟加载
在嵌套查询的基础上 做优化
查询用户数据 将 账户数据一并查询出来了 但是我们的需求只需要用户数据 还查询账户信息的话 影响效率
嵌套查询一共有两条sql
懒加载:(延迟加载,按需加载) : 加载主要对象的时候 是否需要将与之相关的对象查询出来
是: 立即加载 (不管要不要第二张表信息 , 都查询出来)
否: 懒加载(不查询第二张表信息, 什么时候使用到第二张表信息的时候 就查询 )
懒加载的加载时机 : 什么时候使用 什么时候查询 , 只要是涉及到第二张表的任意字段都会查询
主配置文件打开延迟加载策略-全局配置
在主配置文件中打开,默认懒加载配置是关闭的
全局配置:一对一有效一对多也有效
<!--全局的配置信息-->
<settings>
<!--懒加载的配置打开-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
局部配置-覆盖全局(某些特殊的sql特殊设置)
开发中:一般情况配置懒加载即可(全局配置够用了)
查询账户的时候 马上查询用户信息(特殊情况) : 查询的账户信息 对应的就只有一个用户 最多内存中多存入一个对象(可配置也可以不配置)
但是, 如果是一对多的情况, 查询一个用户, 例如订单-购买历史记录…(必须使用懒加载)
<!--对账户信息的配置描述-->
<resultMap id="baseAccountUser" type="account">
<!--主键-->
<id column="id" property="id"></id>
<!--普通配置-->
<result column="money" property="money"></result>
<result column="address" property="address"></result>
<result column="aname" property="aname"></result>
<result column="uid" property="uid"></result>
<!--一对一:外键-->
<association property="user" javaType="user"
select="com.llz.dao.UserDao.findByUid" column="uid" fetchType="eager"></association>
</resultMap>
fetch抓取Type类型 :
eager: 立即加载
lazy: 懒加载
一对多和一对一的配置 , 只要有局部配置(单独的位置覆盖) 会覆盖全局配置
缓存配置
嵌套查询 加载策略 优化
缓存:优化 查询 速度快
数据库的数据存储的位置
数据是保存在本地磁盘(硬盘)
需要读取数据 必然要经过磁盘的IO
目标 减少磁盘的IO 查询效率更高
数据不要保存在硬盘上 保存在内存中即可
缓存:不需要经理磁盘IO ,查询速度高,
缺点:数据保存在缓存中 不持久
缓存主要用于存储查询数据
mybatis分为一级缓存和二级缓存
一级缓存
cache:缓存
默认的不用管,自己进行操作(已经经过了一定的优化)存储,默认就有。
一级缓存存储的位置sqlSession
测试类:
/**
* 查询账户信息
*/
@Test
public void testFindByUid(){
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<Account> accountList1 = accountDao.findById(1);
System.out.println("第一次查询:"+accountList1); //同一个AccountDao 同一个sqlSession产生的
List<Account> accountList2 = accountDao.findById(1);
System.out.println("第二次查询:"+accountList2);//同一个AccountDao 同一个sqlSession产生的
AccountDao accountDao2 = sqlSession.getMapper(AccountDao.class);
List<Account> accountList3 = accountDao2.findById(1); //不同的AccountDao 但是 是由同一个sqlSession产生的
System.out.println("第三次查询:"+accountList3);
//另外一个sqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession();
AccountDao acocuntDao3 = sqlSession2.getMapper(AccountDao.class);
List<Account> accountLis4= acocuntDao3.findById(1);// 不同的AccountDao 不同的sqlSession 但是同一个 sqlSessionFactory
System.out.println("第四次查询:"+accountLis4);
}
二级缓存
比一级缓存更高级: 存储的范围更广(很少用)
麻烦,复杂,效率不怎样 , 存储的位置 sqlSessionFactory 需要手动配置
可配置缓存(开发中不用) , 后续我们会学习专门的缓存技术
手动配置很麻烦
- 程序必须支持二级缓存 官网找配置 默认二级缓存是开启的 cacheEnabled=true 还是配置一下
- 指明哪些配置需要使用二级缓存 找到映射xml文件配置使用二级缓存即可
<!--当前配置文件需要使用二级缓存-->
<cache></cache>
配置会报错
NotSerializableException: com.llz.domain.Account account对象没有实现序列化接口
- 只要想要二级缓存的对象 必须实现序列化接口 -> 将原本的一级缓存数据 转移到二级缓存的位置上 还是sqlSession级别的缓存
- 指明哪一条sql语句需要使用二级缓存
<!--
//根据用户的id 查询用户下对应的账户信息
public List<Account> findById(Integer uid);
useCache="true"> 当前配置使用二级缓存
-->
<select id="findById" parameterType="int" resultType="account" useCache="true">
select * from tbl_account where uid = #{uid}
</select>
- 二级缓存不是自动存储的 必须得手动的往二级缓存中加入数据 否则 只使用一级缓存
- 至少有一次查询数据的时候 将数据结果 commit即可
序列化接口的目的: 1.持久化(序列化) 将数据保存到文件中(本地) 2.数据传输(多个项目之间进行数据交互的方式).
- 至少有一次查询数据的时候 将数据结果 commit即可
properties
当主配置文件写完后 尽量可以使用copy而不做修改更好 而数据源的部分我们是需要发生改变的 数据源值部分需要修改, 如果不修改配置文件 也能够达到替换xml值部分 , 将一部分内容抽取出去
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///day07_02"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
<dataSource type="POOLED">
<property name="driver" value="变量"/>
<property name="url" value="变量"/>
<property name="username" value="变量"/>
<property name="password" value="变量"/>
</dataSource>
图示:
typeAlias
<typeAlias type="com.llz.domain.User" alias="user"/> 单独定义
<!-- 指定实体类 别名扫描包,别名不区分大小写 只不过习惯要求 首字母小写 -->
<package name="com.llz.domain"/> 批量定义
setting
<settings>
<setting name="" value=""/>
<setting name="" value=""/>
<setting name="" value=""/>
</settings>
mappers
配置到xml
<mapper resource="com/llz/dao/UserDao.xml"/>
配置到dao接口(没用过) 注意事项:此方式要求映射文件和接口文件要在同一个包下,并且名字要一样
<mapper class="com.llz.dao.UserDao"/>
配置到包下 : 基于第二种的基础上 注意事项:此方式要求映射文件和接口文件要在同一个包下,并且名字要一样
<package name="com.llz.Dao"/>
transactionManager
transactionMananger:事务管理
<transactionManager type="JDBC"/> 使用jdbc的事务操作
//SqlSession sqlSession = sqlSessionFactory.openSession(); connection.setAutoCommit(false)
//connection.setAutoCommit(true) 自动事务
SqlSession sqlSession = sqlSessionFactory.openSession(true);
dataSource
<!--
type="POOLED" : 使用mybatis的默认数据源
type="UNPOOLED" : 不使用数据源
type="JNDI" : 使用服务数据源 服务器内部统一指定api规则
-->
<dataSource type="POOLED">
<property name="driver" value="${mybatis.driver}"/>
<property name="url" value="${mybatis.url}"/>
<property name="username" value="${mybatis.username}"/>
<property name="password" value="${mybatis.password}"/>
</dataSource>