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 configurationlog4j.rootLogger=DEBUG, stdout# MyBatis logging configuration...log4j.logger.org.mybatis.example.BlogMapper=TRACE# Console output...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
全局的配置文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//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 mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- 先写根标签mappernamespace:目前可以理解为单个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 mapperPUBLIC "-//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)*/@Overridepublic 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_idWHERE 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_idWHERE gender = #{user.gender} AND age = #{userInfo.age}</select>
测试类
/*** 根据条件查询指定用户的数据*/@Overridepublic 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入门编程(重点在明天)
<!-- 定义一个resultMapid:必须和引用该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_user1、第一个判断:如果gender值不为nullWHERE AND gender = "男"2、第二个判断:如果address不为nullAND address = "重庆"
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//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 mapperPUBLIC "-//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接口```javapublic List<OrderCustom> queryOrderAndUser() throws Exception;
mapper.xml配置文件
<!-- 查询订单关联的用户信息 --><select id="queryOrderAndUser" resultType="OrderCustom">SELECTu.id,user_id,create_time,note,user_name,birthday,gender,addressFROM t_order AS o JOIN t_user AS uON o.user_id = u.id;</select>
测试类
@Overridepublic 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 + "]";
} }
- 接口```javapublic List<Order> queryOrderAndUser2()throws Exception;
mapper.xml配置文件
<!-- 自定义resultMaptype:要映射的模型对象,可以使用别名--><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:在主表中声明的对象名称,userjavaType:对象对应的模型类,可以使用别名--><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">SELECTu.id,user_id,create_time,note,user_name,birthday,gender,addressFROM t_order AS o JOIN t_user AS uON 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语句
SELECTo.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,numberFROM t_order AS o JOIN t_user AS uON o.user_id = u.idJOIN 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;}@Overridepublic 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">SELECTo.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,numberFROM t_order AS o JOIN t_user AS uON o.user_id = u.idJOIN t_detail AS d ON o.id = d.order_id;</select>
- 测试类```java@Overridepublic 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语句
SELECTu.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,priceFROM t_user AS u JOIN t_order oON u.id = o.user_idJOIN t_detail AS d ON o.id = d.order_idJOIN 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">SELECTu.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,priceFROM t_user AS u JOIN t_order oON u.id = o.user_idJOIN t_detail AS d ON o.id = d.order_idJOIN t_product AS p ON d.product_id = p.idWHERE u.id = #{userId}</select>
测试类
@Overridepublic 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 mapperPUBLIC "-//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 />
