MyBatis

朝上 2022-01-15 02:31:51 阅读数:788

java mybatis CSDN

MyBatis

一.原生态jdbc编程中的问题总结

1.创建mysql数据

导入下边的脚本:

sql_table.sql:记录表结构

sql_data.sql:记录测试数据,在实际企业开发中,最后提供一个初始化数据脚本

2.原生态jdbc程序

使用jdbc查询mysql数据库中用户表的记录。

修改pom.xml,引入相关依赖

<!-- 引入数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>

程序代码:JdbcTest.java使用jdbc的原始方法(未经封装)实现了查询数据库表记录的操作

public class JdbcTest {

public static void main(String[] args) {

Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {

Class.forName("com.mysql.jdbc.Driver"); // 加载数据库驱动
// 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/order?characterEncoding=utf-8", "root", "root");
String sql = "select from user where username = ?"; // 定义sql语句 ?表示占位符
preparedStatement = connection.prepareStatement(sql); // 获取预处理statement
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
resultSet = preparedStatement.executeQuery();// 向数据库发出sql执行查询,查询出结果集
while (resultSet.next()) {
 // 遍历查询结果集
System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
}
} catch (Exception e) {

e.printStackTrace();
} finally {

// 释放资源
if (resultSet != null) {

try {

resultSet.close();
} catch (SQLException e) {

e.printStackTrace();
}
}
if (preparedStatement != null) {

try {

preparedStatement.close();
} catch (SQLException e) {

e.printStackTrace();
}
}
if (connection != null) {

try {

connection.close();
} catch (SQLException e) {

e.printStackTrace();
}
}
}
}
}

3.问题总结

1)、数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。

设想:使用数据库连接池管理数据库连接。

2)、将sql语句硬编码到java代码中,如果sql 语句修改,需要重新编译java代码,不利于系统维护。

设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。

3)、向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。

设想:将sql语句及占位符号和参数全部配置在xml中。

4)、从ResultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。

设想:将查询的结果集,自动映射成java对象。

二.mybatis框架原理

1.mybatis是什么?

​ mybatis是一个持久层的框架,是apache下的顶级项目。mybatis托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)。

​ mybatis让程序员将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。 mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射

2.mybatis框架原理

在这里插入图片描述

1、 mybatis配置

SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。

mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。

2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂

3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。

4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。

6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。

7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xRpLBkD3-1641215111304)(image/wpsPLJvK4.png)]

三.mybatis入门程序

1.工程结构

1.1 需求

根据用户id(主键)查询用户信息

根据用户名称模糊查询用户信息

添加用户

删除用户

更新用户

1.2 修改pom.xml,引入相关依赖

<!-- 引入mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>

1.3 新建log4j.properties日志属性文件

mybatis默认使用log4j作为输出日志信息。

在src文件夹下,创建了resources文件夹,右击选择Mark Directory as --> resources root,在其里面创建log4j.properties文件如下:

# Global logging configuration
# 在开发环境下日志级别要设置成DEBUG,生产环境设置成info或error
log4j.rootLogger=DEBUG, stdout
# 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

修改pom.xml,添加日志依赖

<!-- 引入日志依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

1.4 新建db.properties属性文件

#mysql
db.username = root
db.password = root
db.jdbcUrl = jdbc:mysql://localhost:3306/order?characterEncoding=utf-8
db.driverClass = com.mysql.jdbc.Driver

修改pom.xml,添加mysql依赖

<!--mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>

1.5 SqlMapConfig.xml核心配置文件

SqlMapConfig.xml是mybatis核心配置文件,配置mybatis的运行环境,数据源、事务等。

在src文件夹下,创建了resources文件夹,在其里面创建SqlMapConfig.xml文件。

首先,先创建mybatis核心配置文件模版

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15WQGLpl-1641215111309)(image/image-20200713211001576.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sisJam0u-1641215111310)(image/image-20200713211254836.png)]

<?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>
</configuration>

要求核心配置文件中,configuration所有配置必须严格按照以下顺序进行编写。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vGnbLOq-1641215111311)(image/image-20210624155159869.png)]

其次,再新建SqlMapConfig.xml核心配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L9pu2OGi-1641215111311)(image/image-20200713211431635.png)]

<?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>
<!-- 加载属性文件 db.properties -->
<properties resource="db.properties"></properties>
<!-- 配置全局参数 -->
<!--<settings></settings>-->
<!-- 自定义别名:扫描指定包下的实体类,给这些类取别名,默认是它的类名或者类名首字母小写 -->
<typeAliases>
<package name="com.cm.entity"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理,事务控制由mybatis-->
<transactionManager type="JDBC" />
<!-- 数据库连接池,由mybatis管理,通过${}直接加载属性文件上的值-->
<!-- -->
<dataSource type="POOLED">
<property name="driver" value="${db.driverClass}" />
<property name="url" value="${db.jdbcUrl}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</dataSource>
</environment>
</environments>
<!-- 批量加载mapper映射文件:指定mapper接口所在的包名,mybatis为其生成代理对象 -->
<mappers>
<package name=" " />
</mappers>
</configuration>

1.6 Mapper映射文件

若想在与同名接口同路径下创建Mapper映射文件,需要修改pom.xml。

原因是IDEA编译后默认会把resources下的文件放到target的classpath下,但是src下的只有Java文件编译生成.class文件放入classpath下,其他文件会忽略的,例如.xml文件。

<build>
<!-- 识别在src/main/java路径下的xml文件 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>

首先,先创建mapper映射文件模版

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OA9QcZRn-1641215111312)(image/image-20200713211001576.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6BuNuEZ1-1641215111314)(image/image-20200713212326790.png)]

<?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映射文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdYqPANS-1641215111315)(image/image-20200713233247884.png)]

1.7 工程结构

image-20200713233645180

2.根据id查询用户

2.1 创建entity.User类

User.java作为mybatis进行sql映射使用,通常属性名与数据库表字段对应

import java.util.Date;
//属性名称和数据库表的字段对应
public class User {

private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日 java.util.Date;
private String address;// 地址
public User() {

}
public User(int id, String username, String sex, Date birthday, String address) {

this.id = id;
this.username = username;
this.sex = sex;
this.birthday = birthday;
this.address = address;
}
public int getId() {

return id;
}
public void setId(int id) {

this.id = id;
}
public String getUsername() {

return username;
}
public void setUsername(String username) {

this.username = username;
}
public String getSex() {

return sex;
}
public void setSex(String sex) {

this.sex = sex;
}
public Date getBirthday() {

return birthday;
}
public void setBirthday(Date birthday) {

this.birthday = birthday;
}
public String getAddress() {

return address;
}
public void setAddress(String address) {

this.address = address;
}
@Override
public String toString() {

return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
", address='" + address + '\'' +
'}';
}
}

2.2 修改映射文件UserMapper.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">
<!-- namespace 命名空间,作用就是对sql进行分类化管理,用于隔离sql语句 注意:将来使用mapper代理方法开发,namespace有特殊重要的作用 -->
<mapper namespace="com.cm.ch01.mapper.UserMapper">
<!-- 根据用户id(主键)查询用户信息 1.id :作为唯一标识 2.parameterType :输入参数映射的类型 3.resultType :输出参数映射的类型,可以直接使用别名 4.? 占位符 : #{} 若是简单数据类型,{}里面的名字可以任意写; 若是引用数据类型,{}里面的名字只能与此类型中的 属性名一致 -->
<select id="selectUserById" parameterType="Integer" resultType="User">
select * from user where id = #{id}
</select>
</mapper>

2.3 创建UserMapper.java接口

/** MyBatis 实现映射关系的规范: 1.Mapper接口和Mapper的xml文件,必须要同名且在同包下 2.Mapper的xml文件中的,namespace的值必须是同名Mapper接口的全类名 3.Mapper接口中的,方法名必须与Mapper的xml中的id一致 4.Mapper接口中的方法的形参类型,必须与Mapper的xml中的parameterType的值一致 5.Mapper接口中的方法的返回值类型,必须与Mapper的xml中的resultType的值一致 */
public interface UserMapper {

//通过id查询用户信息
public User selectUserById(Integer id);
}

2.4 在SqlMapConfig.xml中加载映射文件

在sqlMapConfig.xml中加载User.xml:

 <!-- 批量加载mapper映射文件:指定mapper接口所在的包名,mybatis为其生成代理对象 -->
<mappers>
<package name="com.cm.ch01.mapper" />
</mappers>

2.5 新建junit测试类

1). 修改test目录,右击选择Mark Directory as -> Test Resources Root

img

2). 选择需要建junit测试类的接口,右击选择Go To -> Test

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vs8rMXxE-1641215111316)(image/image-20200713221414296.png)]

3). 创建UserMapperTest类,勾选需要测试的方法

image-20200713222841880

4). 编写测试类

public class UserMapperTest {

//创建会话工厂对象
private SqlSessionFactory factory;
@Before
public void setUp() throws Exception {

//加载mybatis核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void selectUserById() {

SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUserById(1);
System.out.println(user);
sqlSession.close();
}
}

3.根据用户名称模糊查询用户信息

3.1 修改映射文件UserMapper.xml

<!-- 根据用户名进行模糊查询 -->
<select id="selectUserByName" parameterType="String" resultType="User">
<!-- concat()函数,实现拼接 -->
select * from user where username like CONCAT('%',#{username},'%')
</select>

3.2 修改UserMapper.java接口

 //通过名称模糊查询用户信息
public List<User> selectUserByName(String name);

3.3 修改junit测试类

@Test
public void selectUserByName() {

SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.selectUserByName("小明");
for (User user : userList) {

System.out.println(user);
}
sqlSession.close();
}

4.添加用户

4.1 修改映射文件UserMapper.xml

<!-- 添加用户 -->
<insert id="insertUser" parameterType="User">
<!-- 获得当前插入对象的id值 -->
<selectKey keyProperty="id" order="AFTER" resultType="Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>

4.2 修改UserMapper.java接口

//插入用户信息
public void insertUser(User user);

4.3 修改junit测试类

@Test
public void insertUser() throws ParseException {

SqlSession sqlSesison = factory.openSession();
UserMapper userMapper = sqlSesison.getMapper(UserMapper.class);
String str = "2019-09-09 12:12:12";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str);
User user = new User("里斯","2",date,"苏州");
userMapper.insertUser(user);
System.out.println(user);
sqlSesison.commit();
sqlSesison.close();
}

5.修改用户

5.1 修改映射文件UserMapper.xml

<!-- 更新用户 -->
<update id="updateUser" parameterType="User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id = #{id}
</update>

5.2 修改UserMapper.java接口

//更新用户信息
public void updateUser(User user);

5.3 修改junit测试类

@Test
public void updateUser() {

SqlSession sqlSesison = factory.openSession();
UserMapper userMapper = sqlSesison.getMapper(UserMapper.class);
User user = userMapper.selectUserById(29);
user.setUsername("李四");
userMapper.updateUser(user);
sqlSesison.commit();
sqlSesison.close();
}

6.删除用户

6.1 修改映射文件UserMapper.xml

<!-- 删除用户 -->
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>

6.2 修改UserMapper.java接口

//删除用户信息
public void deleteUser(Integer id);

6.3 修改junit测试类

@Test
public void deleteUser() {

SqlSession sqlSesison = factory.openSession();
UserMapper userMapper = sqlSesison.getMapper(UserMapper.class);
userMapper.deleteUser(29);
sqlSesison.commit();
sqlSesison.close();
}

四.SqlMapConfig-配置内容

SqlMapConfig.xml

mybatis的全局配置文件SqlMapConfig.xml,配置内容及顺序如下:

properties(属性)

settings(全局配置参数)

typeAliases(类型别名)

typeHandlers(类型处理器)

objectFactory(对象工厂)

plugins(插件)

environments(环境集合属性对象)

​ environment(环境子属性对象)

​ transactionManager(事务管理)

​ dataSource(数据源)

mappers(映射器)

1. properties属性

需求:

将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。

在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。

将数据库连接参数只配置在db.properties中,原因:方便对参数进行统一管理,其它xml可以引用该db.properties。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wsczsgt-1641215111317)(image/wps1.jpg)]

在sqlMapConfig.xml加载属性文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8OSGx3S-1641215111318)(image/wps2.jpg)]

properties特性:

注意: MyBatis 将按照下面的顺序来加载属性:

在 properties 元素体内定义的属性首先被读取。

<properties resource="db.properties">
<!-- properties中还可以配置一些属性名和属性值 -->
<property name="" value="" />
</properties>

然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。

最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

建议:

不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。

在properties文件中(如:db.properties文件)定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX

2. settings全局参数配置

mybatis框架在运行时可以调整一些运行参数。比如:**开启二级缓存、开启延迟加载。**全局参数将会影响mybatis的运行行为。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n50gBhKh-1641215111318)(image/wps3.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZ8Y2gKJ-1641215111319)(image/wps4.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qtEUdxIL-1641215111320)(image/wps5.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hjLa8XYE-1641215111320)(image/wps6.jpg)]

3. typeAliases 别名

3.1 需求

在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。

如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。

3.2 mybatis默认支持别名

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal

3.3 自定义别名

批量定义别名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dDmdcAF1-1641215111321)(image/wps9.jpg)]

4. mappers(映射配置)

批量加载mapper

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FfyH29uZ-1641215111322)(image/wps13.jpg)]

五.输入映射-parameterType

通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的自定义的包装类型

需求

完成用户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息、其它信息,比如商品、订单的)

1.传递pojo的包装类型

1.1 定义包装类型pojo

针对上边需求,建议使用自定义的包装类型的pojo。在包装类型的pojo中将复杂的查询条件包装进去。

UserQueryVo 用户包装类型 UserCustom继承User是用户的扩展类,扩展用户的信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LS1qAHY8-1641215111322)(image/wps111.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-arTlo4hi-1641215111323)(image/wps222.jpg)]

1.2 mapper.xml

在UserMapper.xml中定义用户信息综合查询(查询条件复杂,通过高级查询进行复杂关联查询)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U4jIGxTO-1641215111323)(image/wps333.jpg)]

1.3 mapper.java

​ 在接口UserMapper.java中,定义用户信息综合查询方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c25sjaS3-1641215111324)(image/wps444.jpg)]

1.4 测试代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BwRV320e-1641215111324)(image/wps555.jpg)]

2.传递hashmap类型

sql映射文件定义如下:

2.1 UserMapper.xml中

<!-- 传递hashmap综合查询用户信息 hashmap别名-->
<select id="findUserByHashmap" parameterType="hashmap" resultType="user">
select from user where id=#{id} and username like '%${username}%'
</select>

上边的#{id}、%${username}% 是hashmap的key。

2.2 测试

Public void testFindUserByHashmap()throws Exception{

//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件Hashmap对象
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
map.put("username", "小明");
//传递Hashmap对象查询用户列表
List<User>list = userMapper.findUserByHashmap(map);
//关闭session
session.close();
}

异常测试:

传递的map中的key和sql中解析的key不一致。

测试结果没有报错,只是通过key获取值为空。

六.输出映射-resultType及resultMap

1.resultType

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。

如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。

只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。

1.1 输出简单类型

1.1.1 需求

用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。

1.1.2 mapper.xml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NzyfRgsh-1641215111325)(image/wps21.jpg)]

1.1.3 mapper.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5vH7HYy-1641215111325)(image/wps22.jpg)]

1.1.4 测试代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7Nv1S4e-1641215111326)(image/wps23.jpg)]

1.1.5 小结

查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。

1.2 输出pojo对象和pojo列表

不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。

在mapper.java指定的方法返回值类型不一样:

1.2.1 输出单个pojo对象,方法返回值是单个对象类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z6ZKQkVr-1641215111326)(image/wps24.jpg)]

1.2.2 输出pojo对象list,方法返回值是List<Pojo>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiaEpQBF-1641215111327)(image/wps25.jpg)]

生成的动态代理对象中是根据mapper.java中的方法的返回值类型确定是调用selectOne(返回单个对象调用)还是selectList (返回集合对象调用 ).

2.resultMap

mybatis中使用resultMap完成高级输出结果映射。

2.1 resultMap使用方法

如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

1、定义resultMap

2、使用resultMap作为statement的输出映射类型

2.2 将下边的sql使用User完成映射

SELECT id a , username b FROM USER WHERE id=#{value} User类中属性名和上边查询列名不一致。

2.2.1定义resultMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0yTObHD6-1641215111327)(image/wps26.jpg)]

2.2.2 使用resultMap作为statement的输出映射类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61qb2TlH-1641215111327)(image/wps27.jpg)]

2.2.3 mapper.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjp4nTgM-1641215111328)(image/wps28.jpg)]

2.2.4 测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vU4nDi4Z-1641215111330)(image/wps29.jpg)]

3. 小结

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

七.动态sql

1.什么是动态sql?

mybatis核心对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。

2.什么是sql片段?

将实现的动态sql的代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段,方便程序员进行开发。

3.需求

1.需求:通过用户性别和姓名查询用户列表 PS:不允许性别或者姓名为null或者空串
2.需求:通过用户性别和姓名查询用户列表条数 PS:不允许性别或者姓名为null或者空串
3.需求:通过用户性别和姓名查询用户列表,且id值是15或20或25 PS:不允许性别或者姓名为null或者空串

4.编写UserCustom类

/** UserCustom继承User是用户的扩展类,扩展用户的信息 */
public class UserCustom extends User{

}

5.编写UserQueryVO类

/** UserQueryVo 用户包装类型 pojo */
public class UserQueryVO {

private UserCustom userCustom;
private List<Integer> ids;
public UserCustom getUserCustom() {

return userCustom;
}
public void setUserCustom(UserCustom userCustom) {

this.userCustom = userCustom;
}
public List<Integer> getIds() {

return ids;
}
public void setIds(List<Integer> ids) {

this.ids = ids;
}
}

6.编写UserMapper.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.cm.ch02.sql.UserMapper">
<!-- sql 片段 :where 标签不放置在sql片段中,可以提高sql片段利用率-->
<sql id="user_query">
<if test="userCustom!=null">
<if test="userCustom.sex!=null and userCustom.sex!=''">
and sex = #{userCustom.sex}
</if>
<if test="userCustom.username!=null and userCustom.username!=''">
and username like concat('%',#{userCustom.username},'%')
</if>
</if>
</sql>
<!-- 输入多个id查询 -->
<sql id="user_query_id">
<if test="ids!=null">
<!-- foreach 迭代拼接 1.collection属性:输入参数中的属性 2.item属性:临时变量名 3.open属性:开始于... 4.close属性:结束于... 5.separator属性:分隔符 -->
<!-- and (id=1 or id=10 or id=22) -->
<foreach collection="ids" item="user_id" open="and (" close=")" separator="or">
id=#{user_id}
</foreach>
<!-- and id in (1,10,22) -->
<!--<foreach collection="ids" open="and id in (" close=")" separator="," item="id"> #{id} </foreach>-->
</if>
</sql>
<!-- 1.需求:通过用户性别和姓名查询用户列表 PS:不允许性别或者姓名为null或者空串 -->
<select id="selectList" parameterType="UserQueryVO" resultType="UserCustom">
select * from user
<where>
<include refid="user_query"></include>
</where>
</select>
<!-- 2.需求:通过用户性别和姓名查询用户列表条数 PS:不允许性别或者姓名为null或者空串 -->
<select id="selectCount" parameterType="UserQueryVO" resultType="int">
select count(*) from user
<where>
<include refid="user_query"></include>
</where>
</select>
<!-- 3.需求:通过用户性别和姓名查询用户列表,且id值是1或10或22 PS:不允许性别或者姓名为null或者空串 -->
<select id="selectByIds" parameterType="UserQueryVO" resultType="UserCustom">
select * from user
<where>
<include refid="user_query"></include>
<include refid="user_query_id"></include>
</where>
</select>
</mapper>

7.编写UserMapper.java接口

import com.cm.entity.UserCustom;
import com.cm.entity.UserQueryVO;
import java.util.List;
public interface UserMapper {

public List<UserCustom> selectList(UserQueryVO vo);
public int selectCount(UserQueryVO vo);
public List<UserCustom> selectByIds(UserQueryVO vo);
}

8.在核心配置文件中添加扫描

<!-- 批量加载mapper映射文件:指定mapper接口所在的包名,mybatis为其生成代理对象 -->
<mappers>
<package name="com.cm.ch02.sql" />
</mappers>

9.测试

public class UserMapperTest {

private SqlSessionFactory factory;
@Before
public void setUp() throws Exception {

InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void selectList() {

SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
userCustom.setUsername("明");
UserQueryVO vo = new UserQueryVO();
vo.setUserCustom(userCustom);
List<UserCustom> list = userMapper.selectList(vo);
for (UserCustom user: list) {

System.out.println(user);
}
sqlSession.close();
}
@Test
public void selectCount() {

SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserCustom userCustom = new UserCustom();
userCustom.setSex("1");
userCustom.setUsername(null);
UserQueryVO vo = new UserQueryVO();
vo.setUserCustom(userCustom);
int count = userMapper.selectCount(vo);
System.out.println(count);
sqlSession.close();
}
@Test
public void selectByIds() {

SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserCustom userCustom = new UserCustom();
userCustom.setSex("");
userCustom.setUsername("明");
List<Integer> ids = new ArrayList<>();
ids.add(15);
ids.add(20);
ids.add(25);
UserQueryVO vo = new UserQueryVO();
vo.setUserCustom(userCustom);
vo.setIds(ids);
List<UserCustom> list = userMapper.selectByIds(vo);
for (UserCustom user: list) {

System.out.println(user);
}
sqlSession.close();
}
}

八.高级映射

1.分析订单商品数据模型

1.1 每张表记录的数据内容

分模块对每张表记录的内容进行熟悉,相当于你学习系统需求(功能)的过程。

用户表:user

​ 记录了购买商品的用户信息

订单表:orders

​ 记录了用户所创建的订单(购买商品的订单)

订单明细表:orderdetail

​ 记录了订单的详细信息即购买商品的信息

商品表:items

​ 记录了商品信息

1.2 每张表重要的字段设置

非空字段、外键字段

1.3 数据库级别表与表之间的关系

外键关系:
1).外键user_id所在的表orders叫做子表;通过外键user_id指向主表(父表)user中的id
2).外键orders_id所在的表orderdetail叫做子表;通过外键orders_id指向主表(父表)orders中的id
3).外键items_id所在的表orderdetail叫做子表;通过外键items_id指向主表(父表)items中的id

1.4 表与表之间的业务关系

在分析表与表之间的业务关系时一定要建立在某个业务意义基础上去分析。

1).先分析数据库级别之间,有关系的表之间的业务关系:

user和orders:
user ---->orders:一个用户可以创建多个订单,一对多
orders --->user:一个订单只由一个用户创建,一对一
orders和orderdetail:
orders --->orderdetail:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系
orderdetail ---> orders:一个订单明细只能包括在一个订单中,一对一
orderdetail和items:
orderdetail --->items:一个订单明细只对应一个商品信息,一对一
items ---> orderdetail:一个商品可以包括在多个订单明细 ,一对多

2).再分析数据库级别之间,没有关系的表之间是否有业务关系:

orders和items:
orders和items之间可以通过orderdetail表建立关系。

1.5 画图分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQP0J6L6-1641215111331)(image/image-20200714004659822.png)]

2.高级映射-一对一查询

2.1 需求

查询订单信息,关联查询创建订单的用户信息

2.2 sql语句

select orders.*,user.sex,user.address,user.username,user.birthday
from orders,user
where orders.user_id = user.id

2.3 使用resultMap映射的思路

使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。

2.4 新建Orders类

import java.util.Date;
/** 订单类 */
public class Orders {

private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
public Orders(Integer id, Integer userId, String number, Date createtime, String note) {

super();
this.id = id;
this.userId = userId;
this.number = number;
this.createtime = createtime;
this.note = note;
}
public Orders() {

super();
// TODO Auto-generated constructor stub
}
public Integer getId() {

return id;
}
public void setId(Integer id) {

this.id = id;
}
public Integer getUserId() {

return userId;
}
public void setUserId(Integer userId) {

this.userId = userId;
}
public String getNumber() {

return number;
}
public void setNumber(String number) {

this.number = number == null ? null : number.trim();
}
public Date getCreatetime() {

return createtime;
}
public void setCreatetime(Date createtime) {

this.createtime = createtime;
}
public String getNote() {

return note;
}
public void setNote(String note) {

this.note = note == null ? null : note.trim();
}
@Override
public String toString() {

return "Orders [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
+ ", note=" + note + "]";
}
}

2.5 新建OrdersCustom类

/** OrdersCustom 订单类的拓展类 */
public class OrdersCustom extends Orders{

//resultMap方式
//一对一 关联用户
private UserCustom userCustom;
public UserCustom getUserCustom() {

return userCustom;
}
public void setUserCustom(UserCustom userCustom) {

this.userCustom = userCustom;
}
@Override
public String toString() {

return super.toString()+"OrdersCustom [userCustom=" + userCustom + "]";
}
}

2.6 创建OrdersUserMapper.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.cm.ch03.oo.OrdersUserMapper">
<!-- 需求:查询订单信息,关联查询创建订单的用户信息 -->
<!-- type属性:真正返回的类型 id属性:作为resultMap唯一标识 -->
<resultMap type="OrdersCustom" id="orderUserResultMap">
<!-- 配置订单信息 id:指定查询列中唯一标识 result:表中普通字段 其中:column 对应表中的列名 property 类映射到OrdersCustom类中的哪个属性 -->
<id column="id" property="id" />
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置用户信息 OrdersCustom关联了UserCustom : 用association体现一对一的映射关系 property属性:将关联查询的用户信息映射到OrdersCustom中的哪个属性 javaType属性:属性的类型,可使用别名 -->
<association property="userCustom" javaType="UserCustom">
<id column="user_id" property="id" />
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="selectOrderUserResultMap" resultMap="orderUserResultMap">
select orders.*,user.sex,user.address,user.username,user.birthday
from orders,user
where orders.user_id = user.id
</select>
</mapper>

2.7 创建OrdersUserMapper.java接口

import com.cm.entity.OrdersCustom;
import java.util.List;
public interface OrdersUserMapper {

//需求:查询订单信息,关联查询创建订单的用户信息 resultMap
public List<OrdersCustom> selectOrderUserResultMap();
}

2.8 在核心配置文件中添加扫描

<!-- 批量加载mapper映射文件:指定mapper接口所在的包名,mybatis为其生成代理对象 -->
<mappers>
<package name="com.cm.ch03.oo" />
</mappers>

2.9 测试类

import java.util.List;
public class OrdersUserMapperTest {

private SqlSessionFactory factory;
@Before
public void setUp() throws Exception {

factory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("sqlMapConfig.xml"));
}
@Test
public void testSelectOrderUserResultMap(){

SqlSession sqlSession = factory.openSession();
OrdersUserMapper mapper = sqlSession.getMapper(OrdersUserMapper.class);
List<OrdersCustom> list = mapper.selectOrderUserResultMap();
for (OrdersCustom ordersCustom : list) {

System.out.println(ordersCustom);
}
sqlSession.close();
}
}

3.高级映射-一对多查询

3.1 需求

查询订单及订单明细的信息

3.2 sql语句

select orders.*,
orderdetail.id od_id ,
orderdetail.items_id,
orderdetail.items_num
from orders,orderdetail
where orders.id = orderdetail.orders_id

3.3 新建订单详情OrderDetail类

/** 订单详情类 */
public class OrderDetail {

private Integer id;
private Integer orderId;
private Integer itemId;
private Integer itemNum;
public Integer getId() {

return id;
}
public void setId(Integer id) {

this.id = id;
}
public Integer getOrderId() {

return orderId;
}
public void setOrderId(Integer orderId) {

this.orderId = orderId;
}
public Integer getItemId() {

return itemId;
}
public void setItemId(Integer itemId) {

this.itemId = itemId;
}
public Integer getItemNum() {

return itemNum;
}
public void setItemNum(Integer itemNum) {

this.itemNum = itemNum;
}
public OrderDetail() {

super();
// TODO Auto-generated constructor stub
}
public OrderDetail(Integer id, Integer orderId, Integer itemId, Integer itemNum) {

super();
this.id = id;
this.orderId = orderId;
this.itemId = itemId;
this.itemNum = itemNum;
}
@Override
public String toString() {

return "OrderDetail [id=" + id + ", orderId=" + orderId + ", itemId=" + itemId + ", itemNum=" + itemNum + "]";
}
}

3.4 新建OrderDetailCustom订单详情拓展类

/** OrderDetail的拓展类 */
public class OrderDetailCustom extends OrderDetail{

}

3.5 修改OrdersCustom类

/** OrdersCustom 订单类的拓展类 */
public class OrdersCustom extends Orders{

//resultMap方式
//一对一
private UserCustom userCustom;
//一对多 一个订单拥有多个订单详情
private List<OrderDetailCustom> detailCustomList;
public UserCustom getUserCustom() {

return userCustom;
}
public void setUserCustom(UserCustom userCustom) {

this.userCustom = userCustom;
}
public List<OrderDetailCustom> getDetailCustomList() {

return detailCustomList;
}
public void setDetailCustomList(List<OrderDetailCustom> detailCustomList) {

this.detailCustomList = detailCustomList;
}
@Override
public String toString() {

return super.toString()+"OrdersCustom{" +
"userCustom=" + userCustom +
", detailCustomList=" + detailCustomList +
'}';
}
}

3.6 创建OrderAndOrderDetailMapper.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.cm.ch04.om.OrderAndOrderDetailMapper">
<!-- 需求:查询订单及订单明细的信息 -->
<resultMap type="OrdersCustom" id="OrdersAndOrderDetailResultMap">
<id column="id" property="id"/>
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!-- collection : 一对多的关联 1.property属性:OrdersCustom中的实现一对多的属性名 2.ofType属性:OrdersCustom中的实现一对多的属性的类型 -->
<collection property="detailCustomList" ofType="OrderDetailCustom">
<id column="od_id" property="id"/>
<result column="id" property="orderId"/>
<result column="items_id" property="itemId"/>
<result column="items_num" property="itemNum"/>
</collection>
</resultMap>
<select id="selectOrderAndOrderDetail" resultMap="OrdersAndOrderDetailResultMap">
select orders.*,
orderdetail.id od_id ,
orderdetail.items_id,
orderdetail.items_num
from orders,orderdetail
where orders.id = orderdetail.orders_id
</select>
<!-- 需求:查询订单及订单明细的信息,及用户信息 -->
<resultMap type="OrdersCustom" id="result" extends="OrdersAndOrderDetailResultMap">
<!-- 订单信息 -->
<!-- 订单详情信息 -->
<!-- 用户信息 : 一对一 -->
<association property="userCustom" javaType="UserCustom">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="selectOrderANDOrderDetailUser" resultMap="result">
select orders.*,
orderdetail.id od_id ,
orderdetail.items_id,
orderdetail.items_num,
user.sex,
user.username,
user.address,
user.birthday
from orders,orderdetail,user
where orders.id = orderdetail.orders_id and orders.user_id = user.id
</select>
</mapper>

3.7 创建OrderAndOrderDetailMapper.java接口

import java.util.List;
public interface OrderAndOrderDetailMapper {

//需求:查询订单及订单明细的信息
public List<OrdersCustom> selectOrderAndOrderDetail();
//需求:查询订单及订单明细的信息,及用户信息
public List<OrdersCustom> selectOrderANDOrderDetailUser();
}

3.8 在核心配置文件中添加扫描

<!-- 批量加载mapper映射文件:指定mapper接口所在的包名,mybatis为其生成代理对象 -->
<mappers>
<package name="com.cm.ch04.om" />
</mappers>

3.9 测试类

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.Before;
import org.junit.Test;
import java.util.List;
public class OrderAndOrderDetailMapperTest {

private SqlSessionFactory factory;
@Before
public void setUp() throws Exception {

factory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("sqlMapConfig.xml"));
}
@Test
public void testSelectOrderAndOrderDetail() {

SqlSession sqlSession = factory.openSession();
OrderAndOrderDetailMapper mapper = sqlSession.getMapper(OrderAndOrderDetailMapper.class);
List<OrdersCustom> list = mapper.selectOrderAndOrderDetail();
for (OrdersCustom ordersCustom : list) {

System.out.println(ordersCustom);
}
System.out.println(list.size());
sqlSession.close();
}
@Test
public void testSelectOrderANDOrderDetailUser(){

SqlSession sqlSession = factory.openSession();
OrderAndOrderDetailMapper mapper = sqlSession.getMapper(OrderAndOrderDetailMapper.class);
List<OrdersCustom> list = mapper.selectOrderANDOrderDetailUser();
for (OrdersCustom ordersCustom : list) {

System.out.println(ordersCustom);
}
System.out.println(list.size());
sqlSession.close();
}
}

4.高级映射-多对多查询

4.1 需求

查询用户及用户购买商品信息

4.2 sql语句

select u.*,
o.id oid, o.number, o.createtime ,
d.id did, d.items_num,
i.id iid, i.name, i.createtime itemsTime ,i.detail,i.price
from user u,orders o,orderdetail d,items i
where u.id = o.user_id and o.id = d.orders_id and d.items_id = i.id

4.3 新建商品Items类

import java.util.Date;
public class Items {

private Integer id;
private String name;
private Float price;
private String detail;
private String pic;
private Date createtime;
public Integer getId() {

return id;
}
public void setId(Integer id) {

this.id = id;
}
public String getName() {

return name;
}
public void setName(String name) {

this.name = name;
}
public Float getPrice() {

return price;
}
public void setPrice(Float price) {

this.price = price;
}
public String getDetail() {

return detail;
}
public void setDetail(String detail) {

this.detail = detail;
}
public String getPic() {

return pic;
}
public void setPic(String pic) {

this.pic = pic;
}
public Date getCreatetime() {

return createtime;
}
public void setCreatetime(Date createtime) {

this.createtime = createtime;
}
public Items() {

super();
// TODO Auto-generated constructor stub
}
public Items(Integer id, String name, Float price, String detail, String pic, Date createtime) {

super();
this.id = id;
this.name = name;
this.price = price;
this.detail = detail;
this.pic = pic;
this.createtime = createtime;
}
@Override
public String toString() {

return "Items [id=" + id + ", name=" + name + ", price=" + price + ", detail=" + detail + ", pic=" + pic
+ ", createtime=" + createtime + "]";
}
}

4.4 新建ItemsCustom商品拓展类

public class ItemsCustom extends Items{

}

4.5 修改OrderDetailCustom订单详情拓展类

/** OrderDetail的拓展类 */
public class OrderDetailCustom extends OrderDetail{

//一个订单详情中拥有一个商品 一对一
private ItemsCustom itemsCustom;
public ItemsCustom getItemsCustom() {

return itemsCustom;
}
public void setItemsCustom(ItemsCustom itemsCustom) {

this.itemsCustom = itemsCustom;
}
}

4.6 修改UserCustom用户拓展类

/** UserCustom继承User是用户的扩展类,扩展用户的信息 */
public class UserCustom extends User{

//用户拥有多个订单 一对多
private List<OrdersCustom> ordersList;
public List<OrdersCustom> getOrdersList() {

return ordersList;
}
public void setOrdersList(List<OrdersCustom> ordersList) {

this.ordersList = ordersList;
}
@Override
public String toString() {

return "UserCustom [toString()=" + super.toString() + "]";
}
}

4.7 创建UserItemsMapper.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.cm.ch05.mm.UserItemsMapper">
<!-- 需求:查询用户及用户购买商品信息 -->
<resultMap type="UserCustom" id="map">
<!-- 用户信息 -->
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 订单信息 一对多 -->
<collection property="ordersList" ofType="OrdersCustom">
<id column="oid" property="id"/>
<result column="id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<!-- 订单详情 一对多 -->
<collection property="detailCustomList" ofType="OrderDetailCustom">
<id column="did" property="id"/>
<result column="oid" property="orderId"/>
<result column="iid" property="itemId"/>
<result column="items_num" property="itemNum"/>
<!-- 商品信息 一对一 -->
<association property="itemsCustom" javaType="ItemsCustom">
<id column="iid" property="id"/>
<result column="name" property="name"/>
<result column="itemsTime" property="createtime"/>
<result column="detail" property="detail"/>
<result column="price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
<select id="selectUserItems" resultMap="map">
select u.*,
o.id oid, o.number, o.createtime ,
d.id did, d.items_num,
i.id iid, i.name, i.createtime itemsTime ,i.detail,i.price
from user u,orders o,orderdetail d,items i
where u.id = o.user_id and o.id = d.orders_id and d.items_id = i.id
</select>
</mapper>

4.8 创建UserItemsMapper接口

import java.util.List;
public interface UserItemsMapper {

//需求:查询用户及用户购买商品信息
public List<UserCustom> selectUserItems();
}

4.9 在核心配置文件中添加扫描

<!-- 批量加载mapper映射文件:指定mapper接口所在的包名,mybatis为其生成代理对象 -->
<mappers>
<package name="com.cm.ch05.mm" />
</mappers>

4.10 测试类

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.Before;
import org.junit.Test;
import java.util.List;
public class UserItemsMapperTest {

private SqlSessionFactory factory;
@Before
public void setUp() throws Exception {

factory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("sqlMapConfig.xml"));
}
@Test
public void testSelectUserItems() {

SqlSession sqlSession = factory.openSession();
UserItemsMapper mapper = sqlSession.getMapper(UserItemsMapper.class);
List<UserCustom> list = mapper.selectUserItems();
for (UserCustom userCustom : list) {

System.out.println("用户号:"+userCustom.getId()+" 用户名: "+userCustom.getUsername());
for(OrdersCustom ordersCustom : userCustom.getOrdersList()){

System.out.println("订单号:"+ordersCustom.getId() +" 用户号:"+ordersCustom.getUserId());
System.out.println("=============");
for(OrderDetailCustom od:ordersCustom.getDetailCustomList()){

System.out.println("订单详情号:"+od.getId()+" 订单号:"+od.getOrderId()+" 商品号:"+od.getItemId());
ItemsCustom item = od.getItemsCustom();
System.out.println("商品号:"+item.getId()+" 商品名称:"+item.getName());
}
}
}
sqlSession.close();
}
}

5.高级映射-延迟加载

1.什么是延迟加载

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能

需求:如果查询订单并且关联查询用户信息。

如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载

延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

2.使用association实现延迟加载

2.1 需求

查询订单并且关联查询用户信息

2.2 mapper.xml

需要定义两个mapper的方法对应的statement。

1、只查询订单信息

SELECT * FROM orders

在查询订单的statement中使用association去延迟加载(执行)下边的satatement(关联查询用户信息)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hvhXcZbV-1641215111332)(image/wps31.jpg)]

2、关联查询用户信息

​ 通过上边查询到的订单信息中user_id去关联查询用户信息

​ 使用UserMapper.xml中的findUserById

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0R4g7HqT-1641215111333)(image/wps32.jpg)]

上边先去执行selectOrderUserLazyloading,当需要去查询用户的时候再去执行findUserById,通过resultMap的定义将延迟加载执行配置起来。

2.3 延迟加载resultMap

使用association中的select指定延迟加载去执行的statement的id。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CnNscYWP-1641215111333)(image/wps33.jpg)]

2.4 mapper.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FJ49jpY3-1641215111333)(image/wps34.jpg)]

2.5 测试

2.5.1 测试思路

1、执行上边mapper方法(selectOrderUserLazyloading),内部去调用com.mybatis.ch21.lazyloading.OrderUserMapper中的selectOrderUserLazyloading,只查询orders信息(单表)。

2、在程序中去遍历上一步骤查询出的List,当我们调用Orders中的getUser方法时,开始进行延迟加载。

3、延迟加载,去调用UserMapper.xml中findUserById这个方法获取用户信息。

2.5.2 延迟加载配置

mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。

在mybatis核心配置文件中配置: lazyLoadingEnabled、aggressiveLazyLoading

设置项 描述 允许值 默认值
lazyLoadingEnabled 全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 true | false false
aggressiveLazyLoading 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 true | false true
lazyLoadTriggerMethods 指定触发延迟加载的对象的方法。 A method name list separated by commas equals,clone,
hashCode,toString

在SqlMapConfig.xml中配置:

<!-- 全局配置参数,需要时再配置 -->
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 指定触发延迟加载的方法:默认equals,toString等会触发 -->
<setting name="lazyLoadTriggerMethods" value=""/>
</settings>
2.5.3 测试代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AyEwIHRF-1641215111334)(image/wps35.jpg)]

3.延迟加载思考

不使用mybatis提供的association及collection中的延迟加载功能,如何实现延迟加载??

实现方法如下:

定义两个mapper方法:

1、查询订单列表

2、根据用户id查询用户信息

实现思路:

先去查询第一个mapper方法,获取订单信息列表

在程序中(service),按需去调用第二个mapper方法去查询用户信息。

总之:

使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。

九.查询缓存

1.一级缓存

1.1 什么是查询缓存

mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

mybaits提供一级缓存,和二级缓存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lToEbQ6A-1641215111334)(image/wps41.jpg)]

**一级缓存是SqlSession级别的缓存。**在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

二级缓存是mapper映射文件级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

为什么要用缓存?

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

1.2 一级缓存

1.2.1 一级缓存工作原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8F1CdZkN-1641215111335)(image/wps42.jpg)]

第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息后,将用户信息存储到一级缓存中。

如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

1.2.2 一级缓存测试

mybatis默认支持一级缓存,不需要在配置文件去配置。

按照上边一级缓存原理步骤去测试。

UserMapper.xml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8hcwcp7-1641215111335)(image/wps43.jpg)]

UserMapper.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHsDg6Pu-1641215111336)(image/wps44.jpg)]

测试类:UserMapperTest.java

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P4D1H65j-1641215111336)(image/wps45.jpg)]

1.2.3 一级缓存应用

正式开发,是将mybatis和spring进行整合开发,事务控制在service中。

一个service方法中包括很多mapper方法调用。

service{

//开始执行时,开启事务,创建SqlSession对象
//第一次调用mapper的方法findUserById(1)
//第二次调用mapper的方法findUserById(1),从一级缓存中取数据
//方法结束,sqlSession关闭
}
service{

//开始执行时,开启事务,创建SqlSession对象
//第一次调用mapper的方法findUserById(1)
//第二次调用mapper的方法findUserById(1),从一级缓存中取数据
//方法结束,sqlSession关闭
}

如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为service方法结束,sqlSession就关闭,一级缓存就清空。

2.二级缓存

2.1 原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ps6hepL3-1641215111336)(image/wps51.jpg)]

首先开启mybatis的二级缓存。

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。

每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。

2.2 开启二级缓存

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

第一步:在核心配置文件SqlMapConfig.xml中加入

<setting name="cacheEnabled" value="true"/>
描述 允许值 默认值
cacheEnabled 对在此配置文件下的所有cache 进行全局性开/关设置。 true false true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uLtJ21mR-1641215111337)(image/wps52.jpg)]

第二步:在UserMapper.xml中开启二缓存

UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2uxme85-1641215111337)(image/wps53.jpg)]

2.3 调用pojo类实现序列化接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RbM4lpwl-1641215111337)(image/wps54.jpg)]

为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一样在内存。

2.4 测试方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9nQF1MS-1641215111338)(image/wps55.jpg)]

2.5 useCache 配置

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="findUserById" parameterType="int" resultType="user" useCache="false">

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-svuW6X4t-1641215111338)(image/wps56.jpg)]

2.6 刷新缓存(清空缓存)

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache=“true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

如下:

<insert id="insertUser" parameterType=" User" flushCache="true">

总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

2.7 二级应用场景

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

2.8 二级缓存局限性

​ mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

十.mybatis和spring整合

1.概念

Spring

 Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

MyBatis

 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。MyBatis是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

2.整合思路

需要Spring通过IOC方式管理SqlSessionFactory。
spring和mybatis整合生成代理对象,使用SqlSessionFactory创建SqlSession.(Sping和mybati整合自动完成)。
持久层的mapper都交由有spring进行管理。

3.准备工作

3.1 创建一个新的Maven的java工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O39YDZQB-1641215111338)(image/image-20210304200206857.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQEaNcRa-1641215111339)(image/image-20210304200316460.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9FaJyVX-1641215111339)(image/image-20210304200506823.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ypkQnQFW-1641215111340)(image/image-20210304200542827.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1oVANOZf-1641215111340)(image/image-20210304200615366.png)]

3.2 新建属性文件

创建resources文件夹,右击选择Mark Directory as 为Resources root

新建db.properties文件

#mysql
db.username = root
db.password = root
db.jdbcUrl = jdbc:mysql://localhost:3306/testmybatis?useUnicode=true&characterEncoding=utf8
db.driverClass = com.mysql.jdbc.Driver

新建log4j.properties文件

# Global logging configuration
# 在开发环境下日志级别要设置成DEBUG,生产环境设置成info或error
log4j.rootLogger=DEBUG, stdout
# 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

3.3 引入相关依赖

Maven引入需要的依赖

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.3.4</spring.version>
</properties>
<dependencies>
<!-- junit5 测试框架 -->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--spring相关包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!--引入mybatis逆向工程依赖-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>

4.整合Spring和MyBatis XML+注解版

4.1 新建MyBatis核心配置文件

在resources文件夹下新建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>
<!-- 全局参数配置:驼峰式命名、二级缓存、延迟加载等 -->
<settings>
<!-- 开启驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 开启别称配置 -->
<typeAliases>
<package name="com.igeek.ssm.pojo"/>
</typeAliases>
</configuration>

4.2 新建Spring的配置文件

在resources文件夹下,新建applicationContext.xml文件

4.2.1 配置数据源

在applicationContext.xml配置数据源、加载外部属性文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1.导入外部属性文件 -->
<context:property-placeholder location="db.properties"></context:property-placeholder>
<!-- 2.配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
<property name="jdbcUrl" value="${db.jdbcUrl}"></property>
<property name="driverClass" value="${db.driverClass}"></property>
</bean>
</beans>

4.2.2 配置SqlSessionFactory

在applicationContext.xml配置会话工厂实例

<!-- 3.创建SqlSessionFactory的bean实例 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 加载MyBatis核心配置文件 -->
<property name="configLocation" value="mybatis/SqlMapConfig.xml"></property>
<!-- 加载数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>

注意:

1.使用IDEA的maven工具进行开发时,会出现spring代理器无法读取mapper配置文件 XXXMaper.xml的问题—— org.apache.ibatis.binding.BindingException,可以在pom.xml中配置下面这句话:

...
<build>
<resources>
<resource>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
</resource>
</resources>
</build>
...

2.若没有添加上述配置,则需要将XxxMapper.xml文件建在resources文件夹下,并在配置SqlSessionFactory时需添加下方属性:

<!-- 加载Mapper.xml映射文件的位置 -->
<property name="mapperLocations">
<list>
<value>classpath:/mapper/*Mapper.xml</value>
</list>
</property>

4.2.3 配置Mapper扫描包

在applicationContext.xml配置Mapper扫描器

<!-- 4.配置mapper的扫描包,生成mapper的代理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定扫描的路径 -->
<property name="basePackage" value="com.igeek.ssm.mapper"></property>
<!-- 装配SqlSesisonFactory会话工厂实例,但是注意用的是value -->
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>

4.2.4 配置事务

在applicationContext.xml配置事务管理器

<!-- 5.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 6.开启注解管理事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

4.3 测试

4.3.1 创建User类

public class User{

//属性名称和数据库表的字段对应
private int id;
private String name;
private String gender;
private Date birthday;
private int age;
private String pic;
private String pwd;
private String state;
...
}

4.3.2 创建UserMapper接口与UserMapper.xml映射文件

public interface UserMapper {

public User findUserById(int id);
public void updateUser(User user);
}
<?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.igeek.ssm.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="User">
select * from user where id = #{id}
</select>
<update id="updateUser" parameterType="User">
update user set name=#{name},gender=#{gender},age=#{age} where id=#{id}
</update>
</mapper>

4.3.3 创建UserService业务逻辑类

@Service
public class UserService {

//在IOC容器中,直接注入UserMapper的bean实例
@Autowired
private UserMapper userMapper;
//通过id查询用户信息
@Transactional(readOnly = true)
public User findUserById(int id){

return userMapper.findUserById(id);
}
//测试事务:更新用户信息
@Transactional
public void updateUser(int id){

User user = userMapper.findUserById(id);
//更改信息
user.setName("aaa");
user.setAge(18);
userMapper.updateUser(user);
//一旦出现异常,要么都做,要么都不做
//int i = 10/0;
//更改信息
user.setName("bbb");
user.setAge(20);
userMapper.updateUser(user);
}
}

4.3.4 创建UserServiceTest测试类

//加载Spring的配置文件
@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
class UserServiceTest {

//从IOC容器中获取UserService的实例bean
@Autowired(required = false)
private UserService userService;
@Test
void findUserById() {

User user = userService.findUserById(1);
System.out.println(user);
}
@Test
void updateUser() {

userService.updateUser(1);
}
}

5.整合Spring和MyBatis JavaConfig+注解版

5.1 在配置类中添加MyBatis的配置

MyBatisConfig.java

package com.igeek.ssm.config;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashSet;
//MyBatisConfig 替代 SqlMapConfig.xml核心配置文件
//@Configuration 声明当前类是一个Java Config的配置类
@org.springframework.context.annotation.Configuration
//开启扫描Mapper接口,为其生成代理对象
@MapperScan(basePackages = "com.igeek.ssm.mapper")
//导入MySpringConfig的配置类信息
@Import(MySpringConfig.class)
public class MyBatisConfig {

//将会话工厂实例加入IOC容器中
@Bean
public SqlSessionFactoryBean factory(DataSource dataSource) throws IOException {

SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
//设置数据源
factory.setDataSource(dataSource);
//自定义别称
factory.setTypeAliasesPackage("com.igeek.ssm.pojo");
//全局参数配置
Configuration configuration = new Configuration();
//设置开启驼峰式命名
configuration.setMapUnderscoreToCamelCase(true);
//开启延迟加载
configuration.setLazyLoadingEnabled(true);
configuration.setAggressiveLazyLoading(false);
configuration.setLazyLoadTriggerMethods(new HashSet<>());
//开启二级缓存
configuration.setCacheEnabled(true);
//配置核心配置文件的信息
factory.setConfiguration(configuration);
//加载Mapper的映射文件
factory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*.xml"));
return factory;
}
}

5.2 在配置类中添加Spring的配置

MySpringConfig.java

package com.igeek.ssm.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
//MySpringConfig替代 applicationContext.xml配置文件
@Configuration
@ComponentScan(basePackages = "com.igeek.ssm")
@PropertySource(value = "db.properties")
@EnableTransactionManagement
public class MySpringConfig {

//将数据源加入IOC容器
@Bean(destroyMethod = "")
public ComboPooledDataSource dataSource(
@Value("${db.username}") String user,
@Value("${db.password}") String password,
@Value("${db.jdbcUrl}") String jdbcUrl,
@Value("${db.driverClass}") String driverClass
) throws PropertyVetoException {

ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setDriverClass(driverClass);
return dataSource;
}
//将事务管理器加入IOC容器
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){

DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
return transactionManager;
}
}

5.3 创建Orders类

public class Orders {

private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
...
}

5.4 创建OrdersMapper接口和OrdersMapper.xml映射文件

public interface OrdersMapper {

public List<Orders> findOrders();
public void updateOrders(Orders orders);
}

在resources文件夹下,mapper文件夹中创建OrdersMapper.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.igeek.ssm.mapper.OrdersMapper">
<select id="findOrders" resultType="Orders">
select * from orders
</select>
<update id="updateOrders" parameterType="Orders">
update orders set number=#{number} where id=#{id}
</update>
</mapper>

5.5 创建OrderService类

@Service
public class OrderService {

@Autowired
private OrdersMapper ordersMapper;
@Transactional(readOnly = true)
public List<Orders> findAllOrders(){

return ordersMapper.findOrders();
}
@Transactional
public void updateOrders(){

Orders orders1 = new Orders();
orders1.setId(3);
orders1.setNumber(UUID.randomUUID().toString().replaceAll("-",""));
ordersMapper.updateOrders(orders1);
//一旦出现异常
//int i = 10/0;
Orders orders2 = new Orders();
orders2.setId(4);
orders2.setNumber(UUID.randomUUID().toString().replaceAll("-",""));
ordersMapper.updateOrders(orders2);
}
}

5.6 创建OrderServiceTest测试类

import com.igeek.ssm.config.MyBatisConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(classes = MyBatisConfig.class)
class OrderServiceTest {

@Autowired(required = false)
private OrderService orderService;
@Test
void findAllOrders() {

System.out.println(orderService.findAllOrders());
System.out.println(orderService.findAllOrders().size());
}
@Test
void updateOrders() {

orderService.updateOrders();
}
}

十一.逆向工程

1.什么是逆向工程?

​ mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程可以针对单表自动生成mybatis执行所需要的代码(mapper.java,mapper.xml、pojo…)企业实际开发中,常用的逆向工程方式:由数据库的表生成java代码。

2.使用逆向工程

2.1 修改pom.xml

<!--引入mybatis逆向工程依赖-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>

2.2 在pom.xml中新建mybatis-generator插件,在plugins标签内添加

注意:这里我们的generatorConfig.xml文件需要正确的路径,前面的${basedir}自动匹配当前路径,不用修改

<!-- mybatis generator 自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.1</version>
<configuration>
<configurationFile>${basedir}/src/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
</plugin>

2.3 在src/resources下,创建generatorConfig.xml文件

注意:文件中的路径都是绝对路径

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--添加数据库的连接jar包的位置,在项目的左边最下的external libraries中找到复制全路径-->
<classPathEntry location="E:\5.JSP+Servlet\project\repo\mysql\mysql-connector-java\5.1.38\mysql-connector-java-5.1.38.jar"/>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/testmybatis" userId="root" password="root">
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.igeek.ssm.pojo" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.igeek.ssm.mapper" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.igeek.ssm.mapper" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定在数据库中已经存在的表名 -->
<table tableName="user"></table>
<table tableName="orders"></table>
<table tableName="orderdetail"></table>
<table tableName="items"></table>
</context>
</generatorConfiguration>

注意:

​ 在 MySQL 8.0 以上的版本中,在生成 User 表的实体类时,Mybatis Generator 会找到多张 User 表,包括 MySQL information schemas 中的多张 User 表,生成了多个 User 相关的实体类。
​ 若要保证只生成自己指定的 database 中的 User 表,首先要在 中的 connectionURL 中指定数据库的实例名,然后在 中添加相关配置信息,即

,即可保证只生成自己需要的 User 类。

2.4 配置maven的启动配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWq3imz4-1641215111341)(image/image-20200714022927793.png)]

编辑配置,添加maven,命令行添加 mybatis-generator:generate -e

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HgizsBrM-1641215111341)(image/image-20200714023238566.png)]

2.5 点击运行按钮即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJGUAm5o-1641215111341)(image/image-20210304203003209.png)]

将会生成com.igeek.ssm.pojo、com.igeek.ssm.mapper包,及pojo类、mapper.java、mapper.xml文件。

2.6 需求:根据商品名称模糊查询信息

测试类

import com.igeek.ssm.pojo.Items;
import com.igeek.ssm.pojo.ItemsExample;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.List;
@SpringJUnitConfig(locations = "classpath:spring/applicationContext.xml")
class ItemsMapperTest {

@Autowired
private ItemsMapper itemsMapper;
@Test
void selectByExample() {

ItemsExample itemsExample = new ItemsExample();
ItemsExample.Criteria criteria = itemsExample.createCriteria();
criteria.andNameLike("%机%");
List<Items> itemsList = itemsMapper.selectByExample(itemsExample);
for (Items items : itemsList) {

System.out.println(items.getName()+" : "+items.getDetail());
}
}
@Test
void updateByExampleWithBLOBs() {

Items items = itemsMapper.selectByPrimaryKey(1);
System.out.println(items.getName()+" : "+items.getDetail());
items.setDetail("不错!");
itemsMapper.updateByPrimaryKeyWithBLOBs(items);
}
}

3.项目结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GV9VaRqG-1641215111342)(image/image-20210304203733368.png)]

十二.作业

1.完成用户的修改和删除功能

2.使用sql片段实现需求

需求:通过性别和姓名查询用户列表,且id值是152025 PS:不允许性别或者姓名为null或者空串
select * from user where sex=1 and username like concat('%','明','%') and (id=15 or id=20 or id=25);

3.完成高级映射

需求:查询订单、订单明细的信息及下单的用户信息

面试:

1.mybatis和hibernate的本质区别和应用场景

1.本质区别:

hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序员写sql,sql语句自动生成了。对sql语句进行优化、修改比较困难的。

mybatis:专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。

2.应用场景:

hibernate的应用场景:

适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。

mybatis的应用场景:

适用与需求变化较多的项目,比如:互联网项目(京东、淘宝。。)

企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择。

2.${}和#{}的区别

<!-- 需求2:根据用户名称模糊查询用户信息 resultType属性:查询结果对应的类型(自动完成映射) 若查询出来的数据是若干条,即返回的是集合,则填写单条记录的数据类型 #{} 相当于之前的占位符?,类似PrepareStatement使用方式,可有有效避免SQL注入的问题 若是简单数据类型,{}里面的名字可以任意写; 若是引用数据类型,{}里面的名字只能与此类型中的属性名一致 ${} 拼接sql语句,类似于Statement使用方式,会造成SQL注入问题 若是简单数据类型,{}里面只能是value,即${value} 当传参时,类似于' or 1=1 or '结构,则会造成SQL注入 -->
<!-- sql拼接 -->
<!--<select id="selectListByLikeName" parameterType="String" resultType="User"> select * from user where username like '%${value}%' </select>-->
<!-- 占位符 -->
<select id="selectListByLikeName" parameterType="String" resultType="User">
select * from user where username like concat('%',#{username},'%')
</select>
@Test
public void selectListByLikeName(){

//1.获得SqlSession会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//2.获得代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//3.执行
List<User> userList = userMapper.selectListByLikeName("明");
/* select * from user where username like '%${value}%' 拼接后,发生SQL注入: select * from user where username like '%' or 1=1 or '%' */
//List<User> userList = userMapper.selectListByLikeName("' or 1=1 or '");
for (User user : userList) {

System.out.println(user);
}
//4.关闭资源
sqlSession.close();
}
版权声明:本文为[朝上]所创,转载请带上原文链接,感谢。 https://blog.csdn.net/m0_51051154/article/details/122292669