注意:
- 实体类需要默认构造器
- 实体类的属性名要么跟数据库的列名保持一致,要么使用resultMap
- 在映射中,property对应的java属性需要区分大小写,column不分大小写
- 在xml配置sql语句需要换行时,需要在行首至少加一个空格,不然在执行中sql语句或许会因为少空格导致语句混乱
- resultMap的column,如果取了别名的话,也要改成别名。column其实就是最后生成的表的列名,以结果为值。
- 在注解开发中,无论需不需要xml或者有没有使用xml开发,但是在resources对应的路径下有对应的xml配置文件,都会报错,这是在Mybatis在内部加载中会出现的。要解决的话,把配置文件删掉或者换到另一个地方。不过一般来说一个dao要么使用全注解开发,要么使用全xml开发,所以不太会出现这种问题
- 不要忘记提交commit事务
Mybatis框架
第一天 :Mybatis入门
第二天 :Mybatis的单表CRUD操作
第三天:Mybatis的深处和多表
第四天 :Mybatis的缓存和注解开发
前言
1、什么是框架?
它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题
使用框架的好处:
框架封装了很多细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。
2、三层架构
3、持久层技术解决方案
Mybatis框架概述
Mybatis的环境搭建
1、导入Mybatis框架的jar包依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
2、导入mysql的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
3、导入日志log4j的依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
4、导入测试junit的依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
创建Mybatis的配置文件
<!-- 先导入协议 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"/>
<property name="username" value="laodu1"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件值的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
创建Mybatis的映射配置文件
<!-- 先导入协议 -->
<?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.itheima.dao.IUserDao">
<!-- 配置查询所有
id:要配置的方法名
resultType : 要封装的类型
-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
</mapper>
在resources导入log4j文件
log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
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
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
架构图
Mybatis的入门案例
public static void main(String[] args) throws Exception{
//1、读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2、创建一个SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3、使用工厂生产一个SqlSession对象
SqlSession session = factory.openSession();
//4、使用Sqlsession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5、使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6、释放资源
session.close();
in.close();
}
Mybatis基于注解的入门案例
在方法前面加入@Select注解
@Select("select * from user")
List<User> findAll();
<!-- 指定映射配置文件的位置,映射配置文件值的是每个dao独立的配置文件
如果使用注解来配置,此处应该使用class属性指定被注解的dao全限定类名
-->
<mappers>
<mapper class="com.itheima.dao.IUserDao"/>
</mappers>
手写Dao实现类
public class UserDaoImpl implements IUserDao {
SqlSessionFactory sessionFactory = null;
public UserDaoImpl(SqlSessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public List<User> findAll() {
SqlSession session = sessionFactory.openSession();
List<User> user = session.selectList("com.itheima.dao.IUserDao.findAll");//通过namespace的全限定类名+方法名定义到配置的sql语句
session.close();
return user;
}
}
//1、读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2、创建一个SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3、使用工厂生产一个SqlSession对象
IUserDao userDao = new UserDaoImpl(factory);
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6、释放资源
in.close();
入门案例的分析
Mybatis开发流程图
Mybatis的参数和返回值
**单表CRUD操作** 映射文件
<?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.itheima.dao.IUserDao">
<!-- 查询所有-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user;
</select>
<!-- 保存用户-->
<insert id="saveUser" parameterType="com.itheima.domain.User">
<!-- 插入操作后,获取插入数据的id -->
<!-- selectKey 将插入数据库的自增id传进对象的Id中
keyProperty :实体类对对象的属性,要与对象的属性名对应
keyColumn : 数据库的类名
resultType : 返回类型
order : 执行的时间,after(插入后执行)
-->
<selectKey keyProperty="id" keyColumn="id" resultType="Integer" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday});
</insert>
<!-- 更新用户-->
<update id="updateUser" parameterType="com.itheima.domain.User">
update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id};
</update>
<!-- 删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>
<!-- 根据id查询用户-->
<select id="findById" parameterType="Integer" resultType="com.itheima.domain.User">
select * from user where id = #{id}
</select>
<!-- 根据Id模糊查询 -->
<select id="findByName" parameterType="String" resultType="com.itheima.domain.User">
<!-- select * from user where username like #{username} -->
select * from user where username like '%${value}%' <!--必须得是value,固定搭配-->
</select>
<!-- 获取用户的总记录条数-->
<select id="findTotal" resultType="Integer">
select count(id) from user;
</select>
</mapper>
测试类
package com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* 测试Mybatis的CRUD操作
*/
public class MybatisTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
@Before//用于在测试方法执行之前执行
public void init() throws IOException {
//1、读取配置文件,生成字节流输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2、获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3、获取SqlSession对象
sqlSession = factory.openSession();
//4、获取dao代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After//用于在测试方法之后执行
public void destory() throws Exception{
//提交事务
sqlSession.commit();
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testfindAll() throws IOException {
//5、执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
/**
* 测试保存操作
*/
@Test
public void testSave() throws IOException{
User user = new User();
user.setUsername("mybatis last insertid");
user.setAddress("北京市顺义区");
user.setSex("男");
user.setBirthday(new Date());
//5、执行保存方法
userDao.saveUser(user);
System.out.println(user);
}
@Test
public void testUpdate() throws IOException{
User user = new User();
user.setId(41);
user.setUsername("老王更新啦");
user.setAddress("北京市顺义区");
user.setSex("女");
user.setBirthday(new Date());
//5、执行更新方法
userDao.updateUser(user);
}
@Test
public void testDelete() throws IOException{
//5、执行删除方法
userDao.deleteUser(44);
}
@Test
public void testFindOne() throws IOException{
//5、执行查询一个方法
User user = userDao.findById(41);
System.out.println(user);
}
/**
* 测试模糊查询操作
* @throws IOException
*/
@Test
public void testFindByName() throws IOException{
// List<User> users = userDao.findByName("%王%");
List<User> users = userDao.findByName("王");
for (User user : users) {
System.out.println(user);
}
}
/**
* 查询总记录条数
* @throws IOException
*/
@Test
public void testFindToal() throws IOException{
System.out.println(userDao.findTotal());
}
}
映射配置文件
<!-- 根据Id模糊查询 -->
<select id="findUserByVo" parameterType="com.itheima.domain.queryVo" resultType="com.itheima.domain.User">
select * from user where username like #{user.username}
</select>
实体类
package com.itheima.domain;
public class queryVo {
User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
接口增加方法
/**
* 根据queryVo中的条件查询
* @param vo
* @return
*/
List<User> findUserByVo(queryVo vo);
测试类
@Test
public void testFindByVo() throws IOException{
// List<User> users = userDao.findByName("%王%");
queryVo vo = new queryVo();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = userDao.findUserByVo(vo);
for (User user2 : users) {
System.out.println(user2);
}
}
解决实体类属性和数据库列名不对应的两种方式:
一、起别名:
select id as userId, username as userName, address as userAddress, sex as userSex, birthday as userBirthday from user
二、配置查询结果的列名和实体类的属性名的对应关系(resultMap)
<resultMap id="userMap" type="com.itheima.domain.User">
<!-- property : 实体类的属性(需要区分大小写)
column : 数据库的列名(不需要区分大小写)
-->
<!-- 主键字段的对应 -->
<id property="userId" column="id"></id>
<!-- 非主键字段的对应 -->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
<!-- 查询所有-->
<select id="findAll" resultMap="userMap">
select * from user;
</select>
Mybatis的dao编写
各种方法的实现类写法
package com.itheima.dao.Impl;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
public class UserDaoImpl implements IUserDao {
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
public List<User> findAll() {
//1、根据factory获取Sqlsession对象
SqlSession session = factory.openSession();
//2、调用Sqlsession中的方法,
List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");//参数就是能获取配置信息的key
//3、释放资源
session.close();
return users;
}
public void saveUser(User user) {
//1、根据factory获取Sqlsession对象
SqlSession session = factory.openSession();
//2、调用Sqlsession中的方法,
session.insert("com.itheima.dao.IUserDao.saveUser", user);
//3、提交事务
session.commit();
//4、释放资源
session.close();
}
public void updateUser(User user) {
//1、根据factory获取Sqlsession对象
SqlSession session = factory.openSession();
//2、调用Sqlsession中的方法,
session.update("com.itheima.dao.IUserDao.updateUser", user);
//3、提交事务
session.commit();
//4、释放资源
session.close();
}
public void deleteUser(Integer UserId) {
//1、根据factory获取Sqlsession对象
SqlSession session = factory.openSession();
//2、调用Sqlsession中的方法,
session.update("com.itheima.dao.IUserDao.deleteUser", UserId);
//3、提交事务
session.commit();
//4、释放资源
session.close();
}
public User findById(Integer userId) {
//1、根据factory获取Sqlsession对象
SqlSession session = factory.openSession();
//2、调用Sqlsession中的方法,
User user = session.selectOne("com.itheima.dao.IUserDao.findById", userId);
//4、释放资源
session.close();
return user;
}
public List<User> findByName(String username) {
return null;
}
public int findTotal() {
return 0;
}
}
package com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.dao.Impl.UserDaoImpl;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* 测试Mybatis的CRUD操作
*/
public class MybatisTest {
private InputStream in;
private IUserDao userDao;
@Before//用于在测试方法执行之前执行
public void init() throws IOException {
//1、读取配置文件,生成字节流输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2、获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3、使用工厂对象,创建dao对象
//4、获取dao代理对象
userDao = new UserDaoImpl(factory);
}
@After//用于在测试方法之后执行
public void destory() throws Exception{
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testfindAll() throws IOException {
//5、执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
/**
* 测试保存操作
*/
@Test
public void testSave() throws IOException{
User user = new User();
user.setUsername("mybatis last insertid2222");
user.setAddress("北京市顺义区");
user.setSex("男");
user.setBirthday(new Date());
//5、执行保存方法
userDao.saveUser(user);
System.out.println(user);
}
@Test
public void testUpdate() throws IOException{
User user = new User();
user.setId(41);
user.setUsername("老王更新啦");
user.setAddress("北京市顺义区");
user.setSex("女");
user.setBirthday(new Date());
//5、执行更新方法
userDao.updateUser(user);
}
@Test
public void testDelete() throws IOException{
//5、执行删除方法
userDao.deleteUser(50);
}
@Test
public void testFindOne() throws IOException{
//5、执行查询一个方法
User user = userDao.findById(41);
System.out.println(user);
}
/**
* 测试模糊查询操作
* @throws IOException
*/
@Test
public void testFindByName() throws IOException{
// List<User> users = userDao.findByName("%王%");
List<User> users = userDao.findByName("王");
for (User user : users) {
System.out.println(user);
}
}
/**
* 查询总记录条数
* @throws IOException
*/
@Test
public void testFindToal() throws IOException{
System.out.println(userDao.findTotal());
}
}
Mybatis配置的细节(几个标签的使用)
properties标签的使用
<!-- 配置properties -->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"/>
<property name="username" value="laodu1"/>
<property name="password" value="123456"/>
</properties>
<!-- 配置环境-->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
另外添加properties文件来使用properties标签
注意:这里的properties文件里的&不用改成&,不然会报错
<!-- 引入外部配置文件 -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
jdbc.username=laodu1
jdbc.password=123456
别名的配置以及Mappers的优化
<!-- 使用typeAliases配置别名,它只能配置domain中类的别名 -->
<typeAliases>
<!-- typeAlises用于配置别名,type属性指定的是实体类全限定类名,alises属性指定别名, 当指定了别名就不再区分大小写
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>-->
<!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名不在区分大小写 -->
<package name="com.itheima.domain"/>
</typeAliases>
<mappers>
<!-- 配置映射文件的位置-->
<!-- package标签是用于指定dao接口所在的包, 当指定之后,就不需要再写mapper以及resource或者class -->
<!-- <mapper resource="com/itheima/dao/IUserDao.xml"></mapper>-->
<package name="com.itheima.dao"/>
</mappers>
Mybatis的连接池
Mybatis中的连接池:
Mybatis连接池提供了3中方法的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式:
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED 采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是没有使用池的思想
JNDI 采用服务器提供的JNDI技术实现,来获取Datasource对象,不同的服务器拿到的DataSource是不一样的
注意:如果不是web获取maven的war工程,是不能使用的
我们课程中使用的是tomcat服务器,采用的连接池就是dbcp连接池。
Mybatis的事务控制及设计的方法
where的两种创建方式(where标签+if标签)
这里要注意了,if标签里的test用的是sql的语法,不能用&&,只能用and
<!-- 根据条件查询 -->
<select id="findUserByCondition" resultMap="userMap" parameterType="com.itheima.domain.User">
<!-- select * from user where 1=1 -->
<!-- <if test="username != null">-->
<!-- and username = #{username}-->
<!-- </if>-->
select * from user where username = #{username}
</select>
<select id="findUserByCondition" resultMap="userMap" parameterType="com.itheima.domain.User">
select * from user
<where>
<if test="username != null">
and username = #{username}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</where>
</select>
foreach标签
<!-- 根据queryvo中的Id集合实现查询用户列表 -->
<select id="findUserInIds" resultMap="userMap" parameterType="com.itheima.domain.queryVo">
select * from user
<where>
<if test="ids != null and ids.size() > 0"></if>
<!--
collection : 代表要遍历的集合元素,注意编写时不要写#{}
open : 代表语句的开始部分
close : 代表结束部分
item : 代表遍历集合的每个元素,生成的遍历名
seperator : 代表分隔符
-->
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</where>
</select>
了解的内容(抽取重复代码块)
<!-- 了解的内容:抽取重复的sql语句 -->
<sql id="defaultUser">
select * from user;
</sql>
<!-- 查询所有-->
<select id="findAll" resultType="user">
<include refid="defaultUser"></include>
<!-- select * from user; -->
</select>
Mybatis的多表查询
新建表account
创建外键和user表对应
一对一
的查询,对于每个account都能同时查到对应的user:
(1)方法一:
在account实体类中加入一个user对象,并加入get和set方法(从表实体应该包含一个主表实体的应用)
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
private User user;
将account里的所有属性全都和列名对应,
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容 -->
<association property="user" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</association>
</resultMap>
结果展示:
(2)方法二:
直接创建一个有所有属性的类,直接进行封装
public class AccountUser extends Account {
private String username;
private String address;
}
<!-- 查询所有账户,并且带有用户名称和地址信息 -->
<select id="findAllAccount" resultType="accountuser">
select a.*, u.username, u.address from account a, user u where u.id = a.uid
</select>
一对多
(查询user时,同时出现对应的所有account信息):
一对多关系映射,主表实体应该包含从表实体的集合引用
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
//一对多关系映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;
}
使用左外连接和对应的resultMap
<!-- 定义User的resultMap -->
<resultMap id="userAccountMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置User对象中accounts集合的映射 -->
<!-- ofType : 要写全限定类名(这里是因为起了别名)-->
<collection property="accounts" ofType="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<!-- 查询所有-->
<select id="findAll" resultMap="userAccountMap">
select * from user u LEFT OUTER JOIN account a on u.id = a.uid
</select>
在数据库中查询是这样的:
可以看到46有两个重复的
而在查询的时候是这样的
Mybatis会自动将重复的信息封装在一起
多对多
多对多其实就是两个一对多
查询角色的信息同时获取用户的信息
public class Role implements Serializable {
private Integer roleID;
private String roleName;
private String roleDesc;
//多对多的关系映射 : 一个角色可以赋予多个用户
private List<User> users;
}
<!-- 定义role表的resultMap -->
<resultMap id="roleMap" type="role">
<id property="roleID" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="roleMap">
select r.ID as rid, r.ROLE_NAME, r.ROLE_DESC, u.* from role r
LEFT OUTER JOIN user_role ur on r.id = ur.rid
LEFT OUTER JOIN user u on ur.uid = u.id
</select>
在Mysql中找出来是这样的:
查找结果是这样的:
相反的,查询用户的信息同时获取角色的信息
主表实体应该包含从表实体的集合引用
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
private List<Role> roles;
}
定义配置文件,更改sql语句
<!-- 定义User的resultMap -->
<resultMap id="userAccountMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<collection property="roles" ofType="role">
<id property="roleID" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<!-- 查询所有-->
<select id="findAll" resultMap="userAccountMap">
select u.*, r.ID as rid, r.ROLE_NAME, r.ROLE_DESC from user u
LEFT OUTER JOIN user_role ur on u.id = ur.uid
LEFT OUTER JOIN role r on ur.rid = r.id
</select>
Mysql查询结果:
Mybatis查询结果:
Mybatis中的加载时间
一对一的延迟加载:由于是延迟加载,user没必要封装,应该在需要用的时候再封装,所以加入select属性,表示查询用户的唯一标志。其实也就是当已经有了account时,然后需要user的时候,会自动调用userdao的findById方法,根据column(这里的column不能省略)的值来查询,也就是根据当前account的uid属性来查询user。
<!-- 一对一的关系映射:配置封装user的内容
select 属性指定的内容:查询用户的唯一标志
-->
<association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById">
</association>
</resultMap>
此时延迟查询还没开,需要到Mybatis的官网的XML配置的settings找到这两个属性:
然后再配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"/>
<property name="username" value="laodu1"/>
<property name="password" value="123456"/>
</properties>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
<!-- 配置环境-->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.itheima.dao"/>
</mappers>
</configuration>
注意,这里的settings标签,必须要到configuration标签下的最上层,不然会报错
结果对比-------未开启延迟查询:
开启延迟查询时:
可以看到开启时,只有在查询到user对象时,才会进行查找。
一对多的延迟查询
本质上,和一对一时差不多的,除了association标签变成了collection标签,但是里面是还是用select 和 column实现。然后打开settings就行了
<!-- 配置User对象中accounts集合的映射 -->
<!-- ofType : 要写全限定类名(这里是因为起了别名)-->
<collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id">
</collection>
两种其实本质上都是将从表的对象的封装交给从表自己实现,所以select里的方法都是从表的。比如要查找user并且显示account的内容,那每次就根据user的某个属性,用accountdao的方法根据这个属性查找到对应的所有account
Mybatis中的一级缓存和二级缓存
一级缓存
public void testFirstLevelCache() {
User user1 = userDao.findById(41);
System.out.println(user1);
// sqlSession.close();
sqlSession.clearCache();//此方法也可以清空缓存
//再次获取SqlSession对象
// sqlSession = factory.openSession();
//
// userDao = sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2);
}
*一级缓存SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(), close()等方法时,就会清空一级缓存。
*
二级缓存:
按照者3步骤
在Mybatis官网的XML配置的settings里找到
在SqlMapConfig里使用settings标签配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
在画红线的地方进行配置
注意
SqlSessionFactory里存放的是数据,当查询时,它会根据数据建立一个对象,所以两次查询得到的对象是不相等的,但是值是相等的。
@Test
public void testFirstLevelCache() {
SqlSession sqlSession1 = factory.openSession();
IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
sqlSession1.close();//一级缓存消失
SqlSession sqlSession2 = factory.openSession();
IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.findById(41);
System.out.println(user2);
sqlSession2.close();
System.out.println(user1 == user2);
}
这两次查询的结果:
可以看到的确是只进行了一次查询,但是两个对象不相等。
Mybatis的注解开发
在之前的xml配置中,我们都需要至少这两个配置文件:
所谓的注解开发,SqlMapConfig还是必不可少的,只是IUserDao.xml不需要了。
注解一共有4个标签:@Select @Insert @Update @Delete,对应了CRUD几个操作。
1、查询
public interface IUserDao {
/**
* 查询所有用户
* @return
* 在Mybatis中针对 CRUD一共有4个注解
* @Select @Insert @Update @Delete
*/
@Select("select * from user")
List<User> findAll();
}
<!-- 如果使用注解来配置,此处应该使用class属性指定被注解的dao全限定类名
-->
<mappers>
<mapper class="com.itheima.dao.IUserDao"/>
</mappers>
这样注解开发就完成了。
下面这张图片你很好的展示了注解开发其实得到了所需的一切。
在底层配置中都是以namespace + id为key, resutlType + sql为value的
剩下三个标签的基本用法
package com.itheima.dao;
import com.itheima.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;
public interface IUserDao {
/**
* 查询所有用户
*
* @return 在Mybatis中针对 CRUD一共有4个注解
* @Select @Insert @Update @Delete
*/
@Select("select * from user")
List<User> findAll();
/**
* 保存用户
* @param user
*/
@Insert("insert into user (username,address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday})")
void saveUser(User user);
/**
* 更新用户
* @param user
*/
@Update("update user set username=#{username}, sex=#{sex}, address=#{address}, birthday=#{birthday} where id = #{id}")
void updateUser(User user);
/**
* 删除用户
* @param userId
*/
@Delete("delete from user where id = #{id}")
void deleteUser(Integer userId);
/**
* 根据id查询用户
* @param userId
* @return
*/
@Select("select * from user where id = #{id}")
User fingById(Integer userId);
/**
* 根据用户名称模糊查询
* @param username
* @return
*/
// @Select("select * from user where username like #{username}")
@Select("select * from user where username like '%${value}%'")
List<User> findUserByName(String username);
/**
* 查询总用户数量
* @return
*/
@Select("select count(*) from user")
int findTotalUser();
}
测试类
package com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class AnnotationCURDTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before
public void Init() throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
session = factory.openSession();
userDao = session.getMapper(IUserDao.class);
}
@After
public void destory() throws Exception {
session.commit();
session.close();
in.close();
}
@Test
public void testSave() {
User user = new User();
user.setUsername("mybatis annotation");
user.setSex("男");
user.setAddress("北京市昌平区");
user.setBirthday(new Date());
userDao.saveUser(user);
}
@Test
public void testUpdate() {
User user = new User();
user.setId(52);
user.setUsername("mybatis annotation22222222");
user.setSex("男");
user.setAddress("北京市昌平区");
user.setBirthday(new Date());
userDao.updateUser(user);
}
@Test
public void testDelete() {
userDao.deleteUser(52);
}
@Test
public void testFindOne() {
User user = userDao.fingById(41);
System.out.println(user);
}
@Test
public void testFindByName() {
List<User> users = userDao.findUserByName("王");
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testFindTotal() {
int total = userDao.findTotalUser();
System.out.println(total);
}
}
注解的resultMap写法
@ResultMap(“userMap”)也是可以的。
一对一的注解配置:和xml配置一样,
@Select("select * from account")
@Results(id="accountMap", value={
//id指的是这个属性是否是id
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid", property = "uid"),
@Result(column = "money", property = "money"),
//column : 根据指定的column来查询,one=@One是一对一
FetchType.EAGER是立即立即查询
@Result(property = "user", column = "uid", one=@One(select="com.itheima.dao.IUserDao.findById", fetchType= FetchType.EAGER))
})
List<Account> findAll();
一对多的注解配置:
@Select("select * from user")
@Results(id="userMap", value={
@Result(id=true, column="id", property = "id"),
@Result(column="username", property = "username"),
@Result(column="sex", property = "sex"),
@Result(column="address", property = "address"),
@Result(column="birthday", property = "birthday"),
@Result(property = "accounts", column = "id",
many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
//指的是延迟查询
fetchType = FetchType.LAZY))
})
List<User> findAll();
一级缓存是默认打开的
二级缓存注解打开方式:
@CacheNamespace(blocking = true)
public interface IUserDao {}
在接口名上方注释就可以用了。