Mybatis学习总结
总结目录
一、前言
以下内容均为观看传智播客Mybatis的个人总结,如有错误,欢迎指正。
二、什么是Mybatis
2.1 概述
mybatis是一个用java编写的持久层框架,它封装了jdbc操作的很多细节,使开发者只需要关注SQL语句本身,而无需关注注册驱动,创建连接等繁杂的过程;而且它实现了ORM(Object Relational Mappging 对象关系映射)思想的结果集封装。
2.2 执行流程
-
加载配置并初始化
- 触发条件:加载配置文件
- 处理过程:将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
-
接收调用请求
- 触发条件:调用Mybatis提供的API
- 传入参数:为SQL的ID和传入参数对象
- 处理过程:将请求传递给下层的请求处理层进行处理。
-
处理操作请求
- 触发条件:API接口层传递请求过来
- 传入参数:为SQL的ID和传入参数对象
三、Mybatis的入门
3.1 环境搭建
- 准备数据库
CREATE DATABASE `mybatis`;
use `mybatis`;
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');
- 创建maven工程(创建过程略)
- 导入坐标
<?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>com.itheima</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<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.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
3.2 入门案例
- 创建实体类
package com.itheima.domain;
public class User implements Serializable{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
...get()/set()/toString()...
}
- 创建dao接口
public interface UserDao {
// @Select("select * from user")
List<User> findAll();
}
- 创建Mybatis的主配置文件:
SqlConfig.xml
<?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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<!-- <mapper class="com.itheima.dao.IUserDao"/> -->
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
- 创建映射配置文件:
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.itheima.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
</mapper>
- 创建测试类
package com.itheima.test;
public class MybatisTest {
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();
}
}
3.3 入门总结
-
注意点
第一个:创建IUserDao.xml 和 UserDao.java时名称是为了和我们之前的知识保持一致。
在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
所以:IUserDao 和UserMapper是一样的
第二个:在idea中创建目录的时候,它和包是不一样的
包在创建时:com.itheima.dao它是三级结构
目录在创建时:com.itheima.dao是一级目录
第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名 -
基于注解的案例:
- 只需要将USerDao与主配置文件中注释的部分取消。
3.4 自定义Mybatis
- 流程分析
- 读取配置文件,根据配置文件创建session工厂,创建session工厂使用了构建者模式。
Resource.java:
public class Resources {
public static InputStream getResourceAsStream(String filePath){
return Resources.class.getClassLoader().getResourceAsStream(filePath);
}
}
SessionFactoryBuilder.java:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream cfg){
Configuration configuration = XMLConfigBuilder.loadConfiguration(cfg);
return new DefaultSqlSessionFactory(configuration);
}
}
DefaultSqlSessionFactory.java
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
- 根据工厂创建session对象。
DefaultSqlSession.java
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
private Connection connection;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
connection = DataSourceUtil.getConnection(configuration);
}
@Override
public <T> T getMapper(Class<T> daoInterfaceClass) {
return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),new Class[]{daoInterfaceClass},new MapperProxy(configuration.getMappers(),connection));
}
@Override
public void close() {
if(connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 根据session对象创建dao接口的代理对象,它使用了代理模式,使用代理对象执行方法。
MapperProxy.java
public class MapperProxy implements InvocationHandler {
//map的key是全限定类名+方法名
private Map<String,Mapper> mappers;
private Connection conn;
public MapperProxy(Map<String,Mapper> mappers, Connection conn){
this.mappers = mappers;
this.conn = conn;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//2.获取方法所在类的名称
String className = method.getDeclaringClass().getName();
//3.组合key
String key = className+"."+methodName;
//4.获取mappers中的Mapper对象
Mapper mapper = mappers.get(key);
//5.判断是否有mapper
if(mapper == null){
throw new IllegalArgumentException("传入的参数有误");
}
//6.调用工具类执行查询所有
return new Executor().selectList(mapper,conn);
}
}
- 配置类:
Configuration.java
package com.itheima.mybatis.cfg;
public class Configuration {
private String driver;
private String url;
private String username;
private String password;
private Map<String,Mapper> mappers = new HashMap<>();;
...set()/get()...
}
Mapper.java
package com.itheima.mybatis.cfg;
public class Mapper {
private String queryString;
private String resultType;
...set()/get()...
}
- utils工具类
- 总结
四、使用Mybatis实现CRUD
4.1 基于代理DAO
-
搭建开发环境
- 搭建入门案例中的开发环境
- 修改dao接口
public interface UserDao { public List<User> findAllUser(); public void insertUesr(User user); public void updataUser(User user); public void deleteUser(Integer id); public User findById(Integer id); public List<User> findByName(String name); }
- 修改测试类
public class MybatisTest { private InputStream in; private SqlSession session; private UserDao userDao; @Before public void init(){ try { in = Resources.getResourceAsStream("SqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory build = builder.build(in); session = build.openSession(); userDao = session.getMapper(UserDao.class); } @After public void destory(){ session.commit(); session.close(); try { in.close(); } catch (IOException e) { e.printStackTrace(); } } }
- 修改配置文件
<?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.UserDao"> <!-- 查询全部 --> <select id="findAllUser" resultType="com.itheima.domain.User"> SELECT * FROM USER ; </select> <!-- 插入操作 --> <insert id="insertUesr" parameterType="com.itheima.domain.User"> INSERT INTO USER (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address}) where id=#{id}; </insert> <!-- 更新操作 --> <update id="updataUser" parameterType="com.itheima.domain.User"> UPDATE USER SET username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}; </update> <!-- 删除操作 --> <delete id="deleteUser" parameterType="Int"> DELETE FROM user WHERE id=#{uid} </delete> <!-- 根据ID查询 --> <select id="findById" parameterType="Int" resultType="com.itheima.domain.User"> SELECT * from USER WHERE id=#{id} </select> <!-- 模糊查询 --> <select id="findByName" parameterType="string" resultType="com.itheima.domain.User"> SELECT * from USER where username LIKE #{name } </select> </mapper>
-
查询操作
-
根据ID查询
测试类中添加下列代码:
@Test public void test05(){ User userid = userDao.findById(49); System.out.println(userid); }
-
查询全部
测试类中添加下列代码:
@Test public void test01() throws IOException { List<User> allUser = userDao.findAllUser(); for (User user : allUser) { System.out.println(user); } }
- 配置参数总结
resultType 属性:于指定结果集的类型。
parameterType 属性:用于指定传入参数的类型。
sql 语句中使用#{}字符:它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。具体的数据是由#{}里面的内容决定的。
#{}中内容的写法:由于数据类型是基本类型,所以此处可以随意写。
-
-
更新操作
- 测试类:
@Test public void test03(){ User user = new User(); user.setUsername("张四"); user.setBirthday(new Date(123346536)); user.setAddress("山东省"); user.setSex("女"); userDao.updataUser(user); }
-
保存操作
- 测试类:
@Test public void test02(){ User user = new User(); user.setUsername("张三"); user.setBirthday(new Date(123346536)); user.setAddress("山东省"); user.setSex("男"); userDao.insertUesr(user); }
- 配置参数总结:
parameterType 属性: 代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
sql 语句中使用#{}字符: 它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。 具体的数据是由#{}里面的内容决定的。
#{}中内容的写法: 由于我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称。 它用的是 ognl 表达式。
ognl 表达式: 它是 apache 提供的一种表达式语言,全称是: Object Graphic Navigation Language 对象图导航语言 它是按照一定的语法格式来获取数据的。 语法格式就是使用 #{对象.对象}的方式 , #{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用 getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user. 而直接写 username。
-
删除操作
- 测试类
@Test public void test04(){ userDao.deleteUser(48); }
-
模糊查询
- 测试类
@Test public void test06(){ List<User> byName = userDao.findByName("%张%"); for (User u : byName ) { System.out.println(u); } }
- 配置文件总结
#{}表示一个占位符号 通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换, #{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类 型值,#{}括号中可以是 value 或其它名称。
表 示 拼 接 s q l 串 通 过 {}表示拼接 sql 串 通过 表示拼接sql串通过{}可以将 parameterType 传入的内容拼接在 sql中且不进行 jdbc 类型转换, 可 以 接 收 简 单 类 型 值 或 p o j o 属 性 值 , 如 果 p a r a m e t e r T y p e 传 输 单 个 简 单 类 型 值 , {}可以接收简 单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值, 可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是 value
4.2 传统Dao开发
- UserDao实现类
public class UserDaoImpl implements UserDao {
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
@Override
public List<User> findAllUser() {
SqlSession session = factory.openSession();
List<User> users = session.selectList("com.itheima.dao.UserDao.findAllUser");
session.close();
return users;
}
@Override
public void insertUesr(User user) {
SqlSession session = factory.openSession();
session.insert("com.itheima.dao.UserDao.insertUesr",user);
session.commit();
session.close();
}
@Override
public void updataUser(User user) {
SqlSession session = factory.openSession();
session.update("com.itheima.dao.UserDao.updataUser",user);
session.commit();
session.close();
}
@Override
public void deleteUser(Integer id) {
SqlSession session = factory.openSession();
session.insert("com.itheima.dao.UserDao.insertUesr",id);
session.commit();
session.close();
}
}
- 测试类
package com.itheima.test;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
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.List;
public class MybatisTest {
private InputStream in;
private UserDao userDao;
@Before
public void init(){
try {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
userDao = new UserDaoImpl(builder.build(in));
}
@After
public void destory(){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test01() {
List<User> allUser = userDao.findAllUser();
for (User user : allUser) {
System.out.println(user);
}
}
}
4.3 SqlMapConfig.xml配置文件
- 配置内容和顺序:
-properties(属性)
–property -settings(全局配置参数)
–setting
-typeAliases(类型别名)
–typeAliase
–package -typeHandlers(类型处理器)
-objectFactory(对象工厂)
-plugins(插件)
-environments(环境集合属性对象)
–environment(环境子属性对象)
—transactionManager(事务管理)
—dataSource(数据源)
-mappers(映射器)
–mapper
–package
-
properties:此标签有两种配置方式
- 第一种
<properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="1234"/> </properties>
-
第二种
首先引入jdbc.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=1234
然后设置properties
<properties resource="jdbc.properties"/>
- 数据源的引入方法
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
-
typeAliases
<typeAliases>
<!--typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就再区分大小写
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>-->
<!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="com.itheima.domain"></package>
</typeAliases>
- mappers
<mappers>
<!-- 注解开发方式 -->
<mapper class="com.itheima.dao.UserDao"></mapper>
<!-- xml开发方式 -->
<mapper resource="com/itheima/dao/UserDao.xml"></mapper>
<!-- 包扫描方式 -->
<package name="com.itheima.dao"/>
</mappers>
五、事务与SQL动态语句
5.1 连接池技术
- Mybatis连接池的分类
在Mybatis中可以将连接池分为一下三类
UNPOOLED:不使用连接池的数据源
POOLED:使用连接池的数据源
JNDI:使用JNDI实现的数据源
- Mybatis数据源的配置
数据源的配置在SqlMapConfig.xml中的environment 的DataSource标签属性中进行配置:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
5.2 事务控制
在mybatis中,为什么 CUD 过程中必须使用 sqlSession.commit()
提交事务?主要原因就是它在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)
方法,这样我们 就必须使用 sqlSession.commit()
方法,相当于使用了 JDBC 中的 connection.commit()
方法实现事务提交。明白这一点后,我们现在一起尝试不进行手动提交,一样实现 CUD 操作。
public class MybatisTest {
private InputStream in;
private SqlSession session;
private UserDao userDao;
@Before
public void init(){
try {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = builder.build(in);
session = build.openSession(true);
userDao = session.getMapper(UserDao.class);
}
@After
public void destory(){
session.commit();
session.close();
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test02(){
User user = new User();
user.setUsername("张三");
user.setBirthday(new Date(123346536));
user.setAddress("山东省德州市");
user.setSex("男");
userDao.insertUesr(user);
}
}
5.3 动态SQL语句
- 使用举例:
<?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.UserDao">
<update id="updataUser" parameterType="com.itheima.domain.QueryVo">
UPDATE USER SET username=#{user.username},birthday=#{user.birthday},sex=#{user.sex},address=#{user.address}
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" item="id" open="id in (" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</update>
<select id="findByParamter" resultType="com.itheima.domain.User" parameterType="com.itheima.domain.User">
SELECT * FROM USER
<where>
<if test="username != null">
username LIKE #{username }
</if>
<if test="sex != null">
and sex=#{sex }
</if>
</where>
</select>
</mapper>
六、多表查询
环境准备:根据下列sql语句创建数据库并创建实体类。
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) default NULL COMMENT '用户编号',
`MONEY` double default NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`ID` int(11) NOT NULL COMMENT '编号',
`ROLE_NAME` varchar(30) default NULL COMMENT '角色名称',
`ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`UID` int(11) NOT NULL COMMENT '用户编号',
`RID` int(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (`UID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user_role`(`UID`,`RID`) values (41,1),(45,1),(41,2);
6.1 一对多
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.itheima.dao.UserDao">
<resultMap id="userMap" type="com.itheima.domain.User">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
<!-- collection 部分定义了用户关联的账户信息。表示关联查询结果集
property="accounts" :关联查询的结果集存储在 User 对象的上哪个属性。
ofType="account" :指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名 -->
<collection property="accounts" ofType="account">
<id column="id" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<select id="findAllUser" resultMap="userMap">
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid
</select>
<select id="findById" parameterType="Int" resultType="com.itheima.domain.User">
SELECT * from USER WHERE id=#{id}
</select>
<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
SELECT * from USER where username LIKE #{name }
</select>
</mapper>
6.2 多对多
account.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.itheima.dao.AccountDao">
<resultMap id="accountMap" type="account">
<id property="id" column="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用于指定从表方的引用实体属性的 -->
<association property="user" javaType="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</association>
</resultMap>
<select id="findAllAccount" resultMap="accountMap">
SELECT u.*,a.id as aid,a.uid,a.money FROM user as u,account as a WHERE a.uid = u.id
</select>
</mapper>
七、延迟加载
7.1 什么是延迟加载?
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
延迟加载的好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速 度要快。
7.2 延迟加载的实现案例
- 使用association
<?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.IAccountDao">
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容
select属性指定的内容:查询用户的唯一标识:
column属性指定的内容:用户根据id查询时,所需要的参数的值
-->
<association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
<!-- 根据用户id查询账户列表 -->
<select id="findAccountByUid" resultType="account">
select * from account where uid = #{uid}
</select>
</mapper>
- 使用collection
<?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">
<!-- 定义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集合的映射 -->
<collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id"></collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
</select>
</mapper>
7.3 缓存
- 一级缓存:一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。 每次执行CUD操作时会清空一级缓存
测试案例:
/**
* 测试一级缓存
*/
@Test
public void testFirstLevelCache(){
User user1 = userDao.findById(41);
System.out.println(user1);
// sqlSession.close();
//再次获取SqlSession对象
// sqlSession = factory.openSession();
sqlSession.clearCache();//此方法也可以清空缓存
userDao = sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2);
}
/**
* 测试缓存的同步
*/
@Test
public void testClearlCache(){
//1.根据id查询用户
User user1 = userDao.findById(41);
System.out.println(user1);
//2.更新用户信息
user1.setUsername("update user clear cache");
user1.setAddress("北京市海淀区");
userDao.updateUser(user1);
//3.再次查询id为41的用户
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2);
}
-
二级缓存:二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
测试案例:
@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); }
八、注解开发
8.1 常用注解
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与
@Result 一起使用,封装多个结果集
@ResultMap:实现引用
@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
8.2 实现案例
-
创建项目(略)
-
引入约束(略)
-
创建数据库以及实现类(略)
-
创建dao
- AccountDao
public interface AccountDao { /** * 查询所有账户,并且获取每个账户所属的用户信息 * @return */ @Select("select * from account") @Results(id="accountMap",value = { @Result(id=true,column = "id",property = "id"), @Result(column = "uid",property = "uid"), @Result(column = "money",property = "money"), @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER)) }) List<Account> findAll(); /** * 根据用户id查询账户信息 * @param userId * @return */ @Select("select * from account where uid = #{userId}") List<Account> findAccountByUid(Integer userId); }
- UserDao
@CacheNamespace(blocking = true) public interface IUserDao { /** * 查询所有用户 * @return */ @Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), @Result(property = "accounts",column = "id", many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) List<User> findAll(); /** * 根据id查询用户 * @param userId * @return */ @Select("select * from user where id=#{id} ") @ResultMap("userMap") User findById(Integer userId); /** * 根据用户名称模糊查询 * @param username * @return */ @Select("select * from user where username like #{username} ") @ResultMap("userMap") List<User> findUserByName(String username); }
-
SqlMapConfig,xml
<?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 resource="jdbcConfig.properties"></properties>
<!--配置开启二级缓存-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!--配置别名-->
<typeAliases>
<package name="com.itheima.domain"></package>
</typeAliases>
<!-- 配置环境-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!-- 指定带有注解的dao接口所在位置 -->
<mappers>
<package name="com.itheima.dao"></package>
</mappers>
</configuration>