一对一:
Emp.java
public class Emp implements Serializable { private int empNo; private String empName; private Boolean sex; private Address address; private Company company; private EmpCard empCard; public Emp() { super(); } public int getEmpNo() { return empNo; } public void setEmpNo(int empNo) { this.empNo = empNo; } }EmpBusiness.java
public class EmpBusiness extends Emp{ private int cardId; private String cardInfo; public int getCardId() { return cardId; } public void setCardId(int cardId) { this.cardId = cardId; } public String getCardInfo() { return cardInfo; } public void setCardInfo(String cardInfo) { this.cardInfo = cardInfo; } @Override public String toString() { return "EmpBusiness [cardId=" + cardId + ", cardInfo=" + cardInfo + ", toString()=" + super.toString() + "]"; } }** 一对一:association**
<select id="queryEmpOrderByMore2" resultMap="addressPlus"> select * from emp </select> <resultMap type="emp" id="addressPlus"> <id property="empNo" column="empno"/> <result property="empName" column="empname"/> <association property="address" javaType="address"> <result property="homeAddress" column="homeaddress"/> <result property="schoolAddress" column="schooladdress"/> </association> </resultMap>一对多:collection
<select id="queryEmpAndCompany" resultMap="queryCompany" parameterType="int"> select s.*,c.* from emp s inner join company c on s.companyid=c.companyid where s.companyid=#{companyid} </select> <resultMap type="company" id="queryCompany"> <id property="companyId" column="companyid"/> <result property="companyName" column="companyname"/> <collection property="emps" ofType="emp"> <id property="empNo" column="empno"/> <result property="empName" column="empname"/> </collection> </resultMap>java:
public class Company implements Serializable { private int companyId; private String companyName; private List<Emp> emps; ..... }java:测试类
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Emp emp = new Emp(); emp.setEmpNo(1); List<Company> company = empMapper.queryEmpAndCompany(1); sqlSession.commit(); System.out.println("qqq"); System.out.println(company); sqlSession.close(); (MyBatis:多对一,多对多的本质就是 一对多的变化)conf.xml:
<settings> <!-- 开启日志,并指定使用的具体日志 --> <setting name="logImpl" value="LOG4J"/> </settings>如果不指定,Mybatis就会根据以下顺序 寻找日志 SLF4J →Apache Commons Logging →Log4j 2 → Log4j →JDK logging
log4j.properties:
log4j.rootLogger=DEBUG, stdout//控制台输出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n日志级别: DEBUG<INFO<WARN<ERROR 如果设置为info,则只显示 info及以上级别的信息; 建议:在开发时设置debug,在运行时设置为info或以上。
可以通过日志信息,相信的阅读mybatis执行情况( 观察mybatis实际执行sql语句 以及SQL中的参数 和返回结果)
一对一、一对多、多对一、多对多 一对多:公司-员工 , 如果不采用延迟加载 (立即加载),查询时会将 一 和多 都查询,公司、公司中中的所有员工。 如果想要 暂时只查询1的一方, 而多的一方 先不查询 而是在需要的时候再去查询 -->延迟加载
如果增加了mapper.xml ,要修改conf.xml配置文件(将新增的mapper.xml加载进去)
通过debug可以发现,
如果程序只需要员工,则只向数据库发送了查询学生的SQL;当我们后续查询员工证,再第二次发送 查询学生证的SQL。 <select id="queryEmpByLazyLoadingOO" resultMap="emp_empCard" > select * from emp </select> <resultMap type="emp" id="emp_empCard"> <id property="empNo" column="empno"/> <result property="empName" column="empname"/> <association property="empCard" javaType="EmpCard" select="com.mapper.EmpCardMapper.queryEmpCard" column="cardid"> <!-- <id property="companyId" column="companyid"/> <result property="companyName" column="companyname"/> --> </association> </resultMap>员工证类mapper.xml
<mapper namespace="com.mapper.EmpCardMapper"> <select id="queryEmpCard" parameterType="int" resultType="EmPCard"> select * from empcard where cardid=#{cardid} </select> </mapper>java:测试类
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); // Emp emp =new Emp(); // emp.setEmpNo(1); List<Emp> emps = empMapper.queryEmpByLazyLoadingOO(); sqlSession.commit(); System.out.println("qqq"); // System.out.println(emps); for (Emp emp : emps) { System.out.println(emp.getEmpName() + "," + emp.getEmpNo()); EmpCard empCard = emp.getEmpCard(); System.out.println(empCard.getCardId() + "," + empCard.getCardInfo()); } sqlSession.close();结果:
延迟加载的步骤:先查公司,按需查询员工 1.开启延迟加载conf.xml配置settings 2.配置mapper.xml 写2个Mapper:
查询公司。companyMapper.xml为主体。
companyMapper.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.mapper.CompanyMapper"> <!-- 延迟加载 一对多 --> <select id="queryEmpAndCompanyOO2" resultMap="queryCompany2" parameterType="int"> select * from company </select> <resultMap type="company" id="queryCompany2"> <id property="companyId" column="companyid"/> <result property="companyName" column="companyname"/> <collection property="emps" ofType="emp" select="com.mapper.EmpMapper.queryEmpByLazyOO2" column="companyid"> <!-- <id property="empNo" column="empno"/> <result property="empName" column="empname"/> --> </collection> </resultMap> </mapper>即查询 学生的sql是通过 select属性指定,并且通过column指定外键
empMapper.xml: <!-- 一对多,延迟加载需要的: 查询公司中的所有员工 --> <select id="queryEmpByLazyOO2" parameterType="int" resultType="emp"> select * from emp where companyid=#{companyid} </select>java测试类:
public static void queryEmpAndCompanyOO2() throws IOException { String resource = "config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); if (inputStream != null) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); CompanyMapper companyMapper = sqlSession.getMapper(CompanyMapper.class); // Emp emp =new Emp(); // emp.setEmpNo(1); List<Company> companys = companyMapper.queryEmpAndCompanyOO2(); sqlSession.commit(); System.out.println("qqq"); // System.out.println(companys); for (Company company : companys) { System.out.println(company.getCompanyName() + "," + company.getCompanyId()); List<Emp> emps = company.getEmps(); // System.out.println(company.getEmps()); for (Emp emp : emps) { System.out.println(emp.getEmpName() + "," + emp.getEmpNo()); } } sqlSession.close(); } else System.out.println("222"); }**sqlSession.commit();**会清空缓存。当然了close()也会
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Emp emp = empMapper.seleteEmp(2); sqlSession.commit(); Emp emp2 = empMapper.seleteEmp(2); System.out.println("qqq"); System.out.println(emp); System.out.println(emp2); sqlSession.close();结果:只加载了一次
根据异常提示:NotSerializableException可知,MyBatis的二级缓存 是将对象 放入硬盘文件中 序列化:内存->硬盘 反序列化:硬盘->内存 准备缓存的对象,必须实现了序列化接口 (如果开启的缓存Namespace=“org.lanqiao.mapper.EmpMapper”),可知序列化对象为Emp,因此需要将Emp序列化 (序列化Emp类,以及Emp的级联属性、和父类) 触发将对象写入二级缓存的时机:SqlSession对象的close()方法。
Mybatis自带二级缓存:【同一个namespace】生成的mapper对象回顾:namespace的值 就是 接口的全类名(包名.类名), 通过接口可以产生代理对象(empMapper对象)
-->namespace决定了empMapper对象的产生 结论:只要产生的xxxMapper对象 来自于同一个namespace,则 这些对象 共享二级缓存。 注意:二级缓存 的范围是同一个namespace, 如果有多个xxMapper.xml的namespace值相同,则通过这些xxxMapper.xml产生的xxMapper对象 仍然共享二级缓存。 禁用 :select标签中useCache="false"例子:
public static void seleteEmp2() throws IOException { String resource = "config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); if (inputStream != null) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Emp emp = empMapper.seleteEmp2(2); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); // sqlSession2.commit(); EmpMapper empMapper2 = sqlSession2.getMapper(EmpMapper.class); Emp emp2 = empMapper2.seleteEmp2(2); System.out.println("qqq"); System.out.println(emp); System.out.println(emp2); sqlSession2.close(); } else System.out.println("222"); }