Mybatis基础
一、原生JDBC的工具类
1.1 示例代码
public class JDBCUtils{
private static String driver;
private static String url;
private static String username;
private static String password;
//使用静态代码块进行初始化
static{
try{
//读取properties配置文件中的数据
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
//创建Properties对象
Properties prop = new Properties();
//读取流到对象中
prop.load(in);
//解析其中的数据
driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
//注册驱动
Class.forName(driver);
}catch(Exception e){
e.printStackTrace();
}
}
//提供链接的方法
public static Connection getConn(){
Connection conn = null;
try{
conn = DriverManager.getConnection(url,username,password);
}catch(Exception e){
e.printStackTrace();
}
return conn;
}
//关闭资源
public static void close(Connection conn,PreparedStatement ps){
if(ps != null){
try{
ps.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
//重载形式关闭资源,查询的时候使用
public static void close(Connection conn,PreparedStatement ps,ResultSet rs){
if(rs != null){
try{
rs.close();
}catch(Exception e){
e.printStackTrace();
}
}
close(conn,ps);
}
}
1.2 问题总结
- 用原生的jdbc需要频繁的创建链接和关闭资源,会对数据库链接造成压力,解决这个问题用连接池
- 用原生的jdbc编写的sql语句属于硬编码,如果对sql语句进行修改,需要修改java原代码,需要重新编译才会生效。解决方案为使用mybatis中的xml配置方式。
使用原生的jdbc填充占位符也属于硬编码,对对象的映射也需要硬编码,在mybatis有映射关系映射,可以查询结束后直接得到对象。
二、mybatis框架
概念:mybatis属于轻量级的ORM框架,主要致力于解决java代码和数据库之间的交互。使用mybatis框架可以做到对象映射关系,传入参数和输出参数都可以以对象为标准。
2.1 mybatis执行流程图
2.2 mybatis组件
SqlSessionFactoryBuilder:创建SqlSessionFactory对象。
- SqlSessionFactory:由上一个组件得到,作用是获取会话(开启会话)
- SqlSession:会话,属于mybatis的执行器,用来发送sql执行,并让Executor执行器进行执行sql并返回结果集。
Sql mapper:一般用来做mapper代理的方式进行sql语句的操作,由java接口和xml配置文件共同组成,也可以是注解的方式组成。(第二阶段开发方式)
三、mybatis入门代码演示
3.1 mybatis环境的搭建
3.1.1 所需要的jar包(核心jar包和依赖jar包)
3.1.2 需要看api,将日志文件和全局配置文件编写好
日志文件
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
全局的配置文件
<?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>
<!-- 数据源环境配置,目前是需要的,整合spring之后,不需要这一块配置 -->
<environments default="development">
<environment id="development">
<!-- 事务的控制,选择默认的jdbc控制事务,整合spring之后,也不需要 -->
<transactionManager type="JDBC"/>
<!-- 数据源配置 -->
<dataSource type="POOLED">
<!-- 数据库驱动 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 链接的路径 -->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8"/>
<!-- 用户名 -->
<property name="username" value="root"/>
<!-- 密码 -->
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
</configuration>
3.2编写对应的mapper.xml配置文件
该文件中时统一编写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:目前可以理解为单个mapper的标记,分割不同的mapper标签配置,到了后面的mapper代理
方式,会有其他更加重要的特殊含义,目前随意取名
-->
<mapper namespace="userTest">
<!-- 查询语句:该条查询语句根据id查询,返回结果只有一个
id:给该条查询语句取一个名字,在java代码调用的时候使用
parameterType:输入参数的数据类型全名
#{id}:表示用来接收输入的参数,可以理解成ps里面的占位符
resultType:输出参数数据类型。如果是映射成model的对象,则需要写全路径名
-->
<select id="queryUserById" parameterType="java.lang.Integer" resultType="com.six.model.User">
SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
</select>
</mapper>
当编写了mapper.xml配置文件之后,必须要在全局的SqlMapConfig.xml文件中进行加载
<mappers>
<mapper resource="mapper/userMapper.xml"/>
</mappers>
3.3 测试类
```java public class UserDao {
public static void main(String[] args) throws Exception {
queryUserById();
}
public static void queryUserById() throws Exception{
//读取全局的配置文件
String path = "SqlMapConfig.xml";
InputStream config = Resources.getResourceAsStream(path);
//创建工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
/*调用方法执行
* 第一个参数:定位要执行哪一条sql语句,namespace的值.查询语句的id值
* 第二个参数:输入的参数数据,理解成实参
*/
User user = (User)sqlSession.selectOne("userTest.queryUserById", 1);
System.out.println(user);
} }
<a name="Iqre7"></a>
## 3.4 mybatis其他的操作
<a name="9ZKfq"></a>
### 3.4.1 查询所有的User数据
- userMapper.xml配置文件
```xml
<!-- 查询所有的user数据,会返回多条user对象
resultType:输出参数映射
-->
<select id="queryAllUser" resultType="com.six.model.User">
SELECT id,user_name,birthday,gender,address FROM t_user
</select>
测试代码
/**
* 查询所有的用户数据,结果集为多条user对象
* @throws Exception
*/
public static void queryAllUser() throws Exception{
//开启会话
SqlSession sqlSession = factory.openSession();
/*
* 调用查询结果集为多条数据的方法
*/
List<User> list = sqlSession.selectList("userTest.queryAllUser");
System.out.println(list);
}
3.4.2 实现模糊查询
userMapper.xml配置文件
<!-- 实现模糊查询
parameterType:String类型
resultType:输入参数类型
${value}:在模糊查询的时候,只能使用$,并且在大括号中只能写value
-->
<select id="queryUserByName" parameterType="java.lang.String" resultType="com.six.model.User">
SELECT id,user_name,birthday,gender,address FROM t_user WHERE user_name LIKE "%${value}%"
</select>
测试代码
/**
* 实现模糊查询
*/
public static void queryUserByName() {
//开启会话
SqlSession session = factory.openSession();
//调用方法查询
List<User> list = session.selectList("userTest.queryUserByName", "小");
System.out.println(list);
}
3.4.3 实现新增用户数据
userMapper.xml配置文件
<!-- 实现用户新增
id:给该新增语句取一个名字
parameterType:需要传入一个对象,将对象中的值传入到数据库中
-->
<insert id="insertUser" parameterType="com.six.model.User">
INSERT INTO t_user(user_name,gender,address) value(#{user_name},#{gender},#{address})
</insert>
测试类
public static void insertUser() {
User user = new User();
user.setUser_name("李华");
user.setGender("男");
user.setAddress("重庆");
//开启会话
SqlSession session = factory.openSession();
//调用方法执行
//返回值表示,数据库中受影响的行数,如果大于0,说明成功
int row = session.insert("userTest.insertUser", user);
System.out.println(row);
//提交一次事务
session.commit();
//关闭会话
session.close();
}
3.4.4 实现更新操作
userMapper.xml配置文件
<!-- 更新数据,根据id更新 -->
<update id="updateUserById" parameterType="com.six.model.User">
UPDATE t_user SET user_name = #{user_name},address = #{address} WHERE id = #{id}
</update>
测试类
public static void updateUserById() {
//开启会话
SqlSession session = factory.openSession();
User user = new User();
user.setId(1);
user.setUser_name("小明明");
user.setAddress("上海");
//执行语句
int row = session.update("userTest.updateUserById", user);
System.out.println(row);
//提交事务
session.commit();
}
3.4.5 删除操作
userMapper.xml配置文件
<!-- 删除语句 -->
<delete id="deleteUserById" parameterType="java.lang.Integer">
DELETE FROM t_user WHERE id = #{id}
</delete>
测试类
public static void deleteUserById() {
//开启会话
SqlSession session = factory.openSession();
int row = session.delete("userTest.deleteUserById", 1);
System.out.println(row);
//提交事务
session.commit();
}
四、使用mybatis实现dao的模式开发
-
五、使用Mapper代理的方式实现开发
组成:mapper接口(dao接口)和mapper.xml配置文件。
要求:
- mapper标签中namespace必须是对应mapper接口的全路径
- id:必须和接口中方法名称一致
- parameterType:必须和接口中方法的形式参数数据类型一致
-
5.1 实现根据用户id查询单个用户数据
mapper接口
/**
*
* @ClassName: UserMapper
* @Description:用户的mapper接口
* @author Alon
* @date 2020年11月20日 上午11:29:29
*
*/
public interface UserMapper {
/**
*
* @Title: queryUserById
* @Description: 根据用户id查询单个用户数据
* @param @param id 用户的id
* @param @return
* @param @throws Exception
* @return User 用户对象
* @throws
*/
public User queryUserById(int id) throws Exception;
}
编写mapper.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在mapper代理里面具有特殊的含义
namespace:必须是对应mapper接口的全路径
-->
<mapper namespace = "com.six.mapper.UserMapper">
<!-- mapper代理中sql语句的要求
id:必须和接口中方法名称一致
parameterType:输入参数必须和接口中方法括号中的形式参数数据类型一致
resultType:输出参数必须和接口中方法返回值数据类型一致
-->
<select id="queryUserById" parameterType="java.lang.Integer" resultType="com.six.model.User">
SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
</select>
</mapper>
dao实现类
/**
*
* @ClassName: UserDaoImpl
* @Description:用户dao的实现类
* @author Alon
* @date 2020年11月20日 上午11:37:04
*
*/
public class UserDaoImpl implements UserMapper{
//声明会话工厂对象
private SqlSessionFactory factory;
public UserDaoImpl() {
try {
String path = "SqlMapConfig.xml";
InputStream config = Resources.getResourceAsStream(path);
factory = new SqlSessionFactoryBuilder().build(config);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
*
* <p>Title: queryUserById</p>
* <p>Description: 根据id查询单个用户的值</p>
* @param id
* @return
* @throws Exception
* @see com.six.mapper.UserMapper#queryUserById(int)
*/
@Override
public User queryUserById(int id) throws Exception {
//开启会话
SqlSession session = factory.openSession();
//获取mapper对象,名字和mapper接口名一致
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUserById(id);
System.out.println(user);
return null;
}
public static void main(String[] args) throws Exception {
new UserDaoImpl().queryUserById(2);
}
}
5.2 读取properties配置文件
在mybatis中,可以在全局的配置文件SqlMapConfig.xml中进行读取properties配置文件,使用的标签为:
- 需要在开始标签里面增加一个属性:resource=”文件路径”
写法:可以在数据源里面通过${key值}获取到properties中的value
<configuration>
<!-- 加载properties配置文件中的内容,已经实现了读取properties配置文件的内容 -->
<properties resource="db.properties"></properties>
<!-- 数据源环境配置,目前是需要的,整合spring之后,不需要这一块配置 -->
<environments default="development">
<environment id="development">
<!-- 事务的控制,选择默认的jdbc控制事务,整合spring之后,也不需要 -->
<transactionManager type="JDBC"/>
<!-- 数据源配置 -->
<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>
<mapper resource="mapper/userMapper.xml"/>
</mappers>
</configuration>
5.3 别名的配置
主要用来配置自定义的pojo类,每次编写输入映射和输出映射的时候,都需要写全路径名,比较麻烦也比较容易出错。
- 可以在全局的配置文件中设置别名,在mapper.xml配置文件中,输入映射和输出映射可以直接写类名
配置文件
<!-- 设置别名 -->
<typeAliases>
<!-- 只能给单个类进行设置别名 -->
<!-- <typeAlias type="com.six.model.User" alias="User"/>-->
<!-- 将整个包下面的类设置为别名 -->
<package name="com.six.model"/>
</typeAliases>
5.4 自定义的POJO类(重点)
问题:前面写的所有的映射关系和sql指令,全部是单表操作,并且每一个单表都有对应的model类,现在需要多张表同时进行查询、删除、更新等操作。特别是查询。
- 现在有两张表,t_user和t_info,标结构如下:
- 现在查询的要求:查询出性别为男,年龄为20的用户信息
sql查询语句:在查询的条件里面有两个条件,gender在t_user表中,age在t_info表中,返回的结果只用user数据。
SELECT * FROM t_user AS u JOIN t_info AS i ON u.id=i.user_id
WHERE gender = "男" AND age = 18
自定义的pojo类
/**
*
* @ClassName: CustomUser
* @Description:扩展的用户类
* @author Alon
* @date 2020年11月20日 下午4:13:34
*
*/
public class CustomUser {
private User user;
private UserInfo userInfo;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public UserInfo getUserInfo() {
return userInfo;
}
public void setUserInfo(UserInfo userInfo) {
this.userInfo = userInfo;
}
}
mapper接口
/**
*
* @Title: queryUser
* @Description:根据指定条件查询用户数据
* @param @return
* @param @throws Exception
* @return User
* @throws
*/
public User queryUser(CustomUser customUser) throws Exception;
mapper.xml配置文件
<!-- 查询语句:查询用户数据对象,
条件1:用户的性别为男
条件2:用户的年龄只能是18岁
-->
<select id="queryUser" parameterType="CustomUser" resultType="User">
SELECT * FROM t_user AS u JOIN t_info AS i ON u.id=i.user_id
WHERE gender = #{user.gender} AND age = #{userInfo.age}
</select>
测试类
/**
* 根据条件查询指定用户的数据
*/
@Override
public User queryUser(CustomUser customUser) throws Exception {
//获取mapper对象
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.queryUser(customUser);
System.out.println(user);
return null;
}
public static void main(String[] args) throws Exception {
CustomUser cu = new CustomUser();
User user = new User();
user.setGender("男");
UserInfo userInfo = new UserInfo();
userInfo.setAge(18);
cu.setUser(user);
cu.setUserInfo(userInfo);
new UserDaoImpl().queryUser(cu);
}
5.5 输入映射和输出映射
输入映射:parameterType和parameterMap,一般使用parameterType,输入映射的参数可以是哪些数据?
基本数据类型、包装类型、自定义的pojo类。
- 输出映射:resultType和resultMap,一般在开发中使用resultMap,
- resultType:可以使用自定义的pojo类,可以是简单数据类型(要保证该条sql语句查询的结果集只能有一行一列的结果),不能实现延迟加载。
- resultMap:可以使用自定义的pojo类,可以使用一对一、一对多、多对多的级联查询,可以完成模型中属性名和数据库字段名不一致情况下使用。可以实现延迟加载。
resultMap入门编程(重点在明天)
<!-- 定义一个resultMap
id:必须和引用该resultMap的查询语句里面resultMap值一致
type:数据类型,表示的要映射到哪个模型类,可以使用别名
-->
<resultMap type="User" id="queryUserOneMap">
<!-- id标签
对应数据库表中主键的映射关系
column:表中主键字段的名称
property:对应模型中的属性名
-->
<id column="id" property="id"/>
<!-- result
对应数据库表中除了主键之外的其他字段
column:表中其他字段的名称
property:对应模型中的属性名
-->
<result column="user_name" property="userName"/>
<result column="gender" property="gender"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</resultMap>
<!-- 查询单个用户,根据id -->
<select id="queryUserOne" parameterType="int" resultMap="queryUserOneMap">
SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
</select>
5.6 sql片段
在实际开发中,很多业务需求,需要做sql拼接,一般用在高级查询。作用是:按需拼接sql语句,语句都写在配置文件中,可以进行参数的判断,如果该参数不为null,有值的情况下,将sql语句加上去。
举例:
一条完整的sql语句
SELECT * FROM t_user WHERE gender = "男" AND address = "重庆"
可以使用sql片段进行拼接
主查询语句
SELECT 字段 FROM t_user
1、第一个判断:如果gender值不为null
WHERE AND gender = "男"
2、第二个判断:如果address不为null
AND address = "重庆"
<?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.six.mapper.UserMapper">
<!-- 实现多条件查询sql语句 -->
<select id="queryUser" parameterType="User" resultType="User">
SELECT id,user_name,birthday,gender,address FROM t_user
<where>
<if test="gender != null and gender != ''">
AND gender=#{gender}
</if>
<if test="address != null and address != ''">
AND address = #{address}
</if>
</where>
</select>
</mapper>
抽取之后的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.six.mapper.UserMapper">
<!-- 将sql片段进行抽取 -->
<sql id="genderAndaddress">
<if test="gender != null and gender != ''">
AND gender=#{gender}
</if>
<if test="address != null and address != ''">
AND address = #{address}
</if>
</sql>
<!-- 实现多条件查询sql语句 -->
<select id="queryUser" parameterType="User" resultType="User">
SELECT id,user_name,birthday,gender,address FROM t_user
<where>
<include refid="genderAndaddress"></include>
</where>
</select>
</mapper>
5.7 mybatis中的映射关系
5.7.1 一对一的映射关系
举例:查询订单中关联的用户信息
- 主表:订单表t_order,附表:用户表t_user
方式1:结果集用resultType来接收
需要生成一个新的模型类,以主表为父类,将附表对应模型类中的字段重新编写一次,生成为新的模型扩展类(OrderCustom)
生成的扩展模型类 ```java public class OrderCustom extends Order{
private String user_name; private String gender; private String birthday; private String address; public String getUser_name() { return user_name; } public void setUser_name(String user_name) { this.user_name = user_name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return “OrderCustom [user_name=” + user_name + “, gender=” + gender + “, birthday=” + birthday + “, address=”
+ address + ", getId()=" + getId() + ", getUser_id()=" + getUser_id() + ", getCreate_time()="
+ getCreate_time() + ", getNote()=" + getNote() + "]";
}
}
- mapper接口
```java
public List<OrderCustom> queryOrderAndUser() throws Exception;
mapper.xml配置文件
<!-- 查询订单关联的用户信息 -->
<select id="queryOrderAndUser" resultType="OrderCustom">
SELECT
u.id,
user_id,
create_time,
note,
user_name,
birthday,
gender,
address
FROM t_order AS o JOIN t_user AS u
ON o.user_id = u.id;
</select>
测试类
@Override
public List<OrderCustom> queryOrderAndUser() throws Exception {
List<OrderCustom> list = mapper.queryOrderAndUser();
System.out.println(list);
return null;
}
方式2:结果集用resultMap来接收
需要解决的问题:需要用一个模型类来接受两个表中查询的结果集,而且要以主表为返回结果集
解决方式:在主表对应的模型中引入附表对应的模型对象
模型类:主要在Order模型中引入了User对象 ```java public class Order { private int id; private int user_id; private String create_time; private String note; private User user;
public User getUser() { return user; } public void setUser(User user) { this.user = user; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getCreate_time() { return create_time; } public void setCreate_time(String create_time) { this.create_time = create_time; } public String getNote() { return note; } public void setNote(String note) { this.note = note; }
@Override public String toString() { return “Order [id=” + id + “, user_id=” + user_id + “, create_time=” + create_time + “, note=” + note
+ ", user=" + user + "]";
} }
- 接口
```java
public List<Order> queryOrderAndUser2()throws Exception;
mapper.xml配置文件
<!-- 自定义resultMap
type:要映射的模型对象,可以使用别名
-->
<resultMap type="Order" id="queryOrderAndUser2Map">
<!-- 主表order映射关系写好
id:主表中主键的关系映射(关系:数据库字段和模型类中的属性名)
column:数据库中(查询结果集,如果取了别名是生效)的字段
property:模型中的属性名
-->
<id column="id" property="id"/>
<!-- 如果模型中的属性名和查询结果集中的字段名一致,可以省略
result:表示主表中除了主键之外的普通字段映射关系
column:数据库中(查询结果集,如果取了别名是生效)的字段
property:模型中的属性名
-->
<result column="user_id" property="user_id"/>
<result column="create_time" property="create_time"/>
<result column="note" property="note"/>
<!-- 配置在order模型中引入的user对象关系映射:一对一
使用association标签完成一对一的关系映射
property:在主表中声明的对象名称,user
javaType:对象对应的模型类,可以使用别名
-->
<association property="user" javaType="User">
<!-- 主键配置 -->
<id column="user_id" property="id"/>
<!-- 其他普通字段映射 -->
<result column="user_name" property="user_name"/>
<result column="birthday" property="birthday"/>
<result column="gender" property="gender"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- 查询所有订单对应的用户信息,返回结果用resultMap方式,
如果使用resultMap进行返回集映射,必须单独配置一个resultMap标签
-->
<select id="queryOrderAndUser2" resultMap="queryOrderAndUser2Map">
SELECT
u.id,
user_id,
create_time,
note,
user_name,
birthday,
gender,
address
FROM t_order AS o JOIN t_user AS u
ON o.user_id = u.id;
</select>
测试类
public List<Order> queryOrderAndUser2() throws Exception {
List<Order> list = mapper.queryOrderAndUser2();
System.out.println(list);
return null;
}
5.7.2 一对多关系映射
举例:查询订单和订单明细及用户数据
主表:订单表t_order,
附表:订单明细表t_detail和用户表t_user
模型的操作:
在Order类中添加明细的声明的对象(一对多),再添加用户声明的对象。
编写SQL语句
SELECT
o.id,
user_id,
o.create_time AS order_create_time,
note,
user_name,
birthday,
gender,
address,
d.id AS detail_id,
product_id,
d.create_time AS detail_create_time,
items_id,
number
FROM t_order AS o JOIN t_user AS u
ON o.user_id = u.id
JOIN t_detail AS d ON o.id = d.order_id;
修改的Order类模型
public class Order {
private int id;
private int user_id;
private String create_time;
private String note;
private User user;
private List<Detail> detailList;
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> detailList) {
this.detailList = detailList;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getCreate_time() {
return create_time;
}
public void setCreate_time(String create_time) {
this.create_time = create_time;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
@Override
public String toString() {
return "Order [id=" + id + ", user_id=" + user_id + ", create_time=" + create_time + ", note=" + note
+ ", user=" + user + ", detailList=" + detailList + "]";
}
}
mapper接口
public List<Order> queryOrderAndDetail() throws Exception;
mapper.xml配置文件 ```xml
<!-- 查询订单和订单明细及用户信息,只能选择resultMap -->
<select id="queryOrderAndDetail" resultMap="queryOrderAndDetailMap">
SELECT
o.id,
user_id,
o.create_time AS order_create_time,
note,
user_name,
birthday,
gender,
address,
d.id AS detail_id,
product_id,
d.create_time AS detail_create_time,
items_id,
number
FROM t_order AS o JOIN t_user AS u
ON o.user_id = u.id
JOIN t_detail AS d ON o.id = d.order_id;
</select>
- 测试类
```java
@Override
public List<Order> queryOrderAndDetail() throws Exception {
List<Order> list = mapper.queryOrderAndDetail();
System.out.println(list);
return null;
}
5.7.3 多对多的映射关系
举例:查询用户购买的商品信息,用户和商品表之间没有直接的关联信息,在用户表中添加订单对象(声明成集合)、在订单模型中添加订单明细对象(声明成集合)、在订单明细中声明商品对象(单个对象)
主表:用户表t_user
附表:订单表t_order,明细表:t_detail,商品表:t_product
- 改变后的模型类
用户模型:
private int id;
private String user_name;
private String gender;
private String birthday;
private String address;
//声明订单
private List<Order> listOrder;
订单模型
private int id;
private int user_id;
private String create_time;
private String note;
//声明订单明细
private List<Detail> detailList;
订单明细模型
private int id;
private int order_id;
private int product_id;
private String create_time;
private int items_id;
private int number;
//声明一个商品
private Product product;
编写sql语句
SELECT
u.id,
user_name,
birthday,
gender,
address,
o.id AS order_id,
o.create_time AS order_create_time,
note,
d.id AS detail_id,
product_id,
d.create_time AS detail_create_time,
items_id,
d.number AS detail_number,
product_name,
p.number AS product_number,
add_time,
price
FROM t_user AS u JOIN t_order o
ON u.id = o.user_id
JOIN t_detail AS d ON o.id = d.order_id
JOIN t_product AS p ON d.product_id = p.id
mapper接口
public List<User> queryUserAndProduct(int userId) throws Exception;
mapper.xml配置文件
<!-- 自定义resultMpa -->
<resultMap type="User" id="queryUserAndProductMap">
<!-- 主表中的映射关系 -->
<id column="id" property="id"/>
<result column="user_name" property="user_name"/>
<result column="birthday" property="birthday"/>
<result column="gender" property="gender"/>
<result column="address" property="address"/>
<!-- 配置user模型中对应的订单对象,一对多 -->
<collection property="listOrder" ofType="Order">
<id column="order_id" property="id"/>
<result column="order_create_time" property="create_time"/>
<result column="note" property="note"/>
<!-- 配置Order模型中对应的订单明细对象,一对多 -->
<collection property="detailList" ofType="Detail">
<id column="detail_id" property="id"/>
<result column="product_id" property="product_id"/>
<result column="detail_create_time" property="create_time"/>
<result column="items_id" property="items_id"/>
<result column="detail_number" property="number"/>
<!-- 配置在Detail模型中对应的商品对象,一对一 -->
<association property="product" javaType="Product">
<id column="product_id" property="id"/>
<result column="product_name" property="product_name"/>
<result column="product_number" property="number"/>
<result column="add_time" property="add_time"/>
<result column="price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
<!-- 查询用户购买了哪些商品 -->
<select id="queryUserAndProduct" parameterType="java.lang.Integer" resultMap="queryUserAndProductMap">
SELECT
u.id,
user_name,
birthday,
gender,
address,
o.id AS order_id,
o.create_time AS order_create_time,
note,
d.id AS detail_id,
product_id,
d.create_time AS detail_create_time,
items_id,
d.number AS detail_number,
product_name,
p.number AS product_number,
add_time,
price
FROM t_user AS u JOIN t_order o
ON u.id = o.user_id
JOIN t_detail AS d ON o.id = d.order_id
JOIN t_product AS p ON d.product_id = p.id
WHERE u.id = #{userId}
</select>
测试类
@Override
public List<User> queryUserAndProduct(int userId) throws Exception {
List<User> list = mapper.queryUserAndProduct(userId);
System.out.println(list);
return null;
}
六、延迟加载
什么是延迟加载?
一般也称为按需加载,复杂的语句都是有结构层次的,如果是多表查询,可以先查询主表中的内容,只有在业务逻辑中使用了附表中数据的时候,才会继续执行查询附表中的内容。
- 举例说明,查询订单对应的用户数据
第一层查询:只查询订单数据即可
第二层查询:用第一层查询结果里面user_id作为条件去筛选用户表中的数据
- 要使用延迟加载的条件
1、只能使用resultMap,在resultMap里面的association和collection两个标签才具备延迟加载的功能。
2、还需要在全局配置文件中,将延迟加载打开,需要使用到全局配置文件中的
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
mapper.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.six.mapper.OrderMapper">
<!-- 查询用户语句,根据user_id和订单表关联
id:如果是延迟加载需要的查询语句,必须和association或者collection标签中的select值一致
-->
<select id="queryUserById" parameterType="java.lang.Integer" resultType="User">
SELECT id,user_name,birthday,gender,address FROM t_user WHERE id = #{id}
</select>
<!-- 编写自定义的resultMap -->
<resultMap type="Order" id="queryOrderAndUserMap">
<!-- 关联映射主表数据 -->
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="create_time" property="create_time"/>
<result column="note" property="note"/>
<!-- 关联user映射对象关系
property:在order类中声明的用户对象名
javaType:user对象对应的model全路径,可以使用别名
select:调用另外一个select标签中的代码(调用查询用户数据的select),名字自定义
column:要将主表查询结果集中哪个字段的值作为下一条查询语句的条件
-->
<association property="user" javaType="User" select="queryUserById" column="user_id"></association>
</resultMap>
<!-- 查询语句:查询订单所对应的用数据,要使用延迟加载 -->
<select id="queryOrderAndUser" resultMap="queryOrderAndUserMap">
SELECT id,user_id,create_time,note FROM t_order
</select>
</mapper>
七、mybatis缓存机制
概念:在mybatis中,缓存机制的作用是提高查询效率,模式:第一次查询去数据库中查询,将查询的结果集保存在对应的缓存内存中,第二次相同的查询语句,则直接在内存中拿取数据。
7.1 一级缓存
概念:存储的位置是在session层面,一次会话会产生独立的缓存空间。中间如果出现了提交事务的操作,那么会清空缓存中的内容。
-
7.2 二级缓存
概念:存储的位置是在Mapper层面,通过每一个mapper的namespace进行识别,模式:第一次查询去数据库中查询,会将结果保存在对应的缓存内存中,第二次有相同的查询语句,则直接在内存中拿数据。
- 在mybatis中二级缓存需要手动开启。需要在全局的配置文件中用settings标签设置,也需要在对应的mapper中添加
标签。 - 需要在对映射的模型类实现序列化
//在SqlMapConfig.xml配置文件中
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
//对应的mapper.xml文件中,在mapper的第一行
<cache />