SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第二天)Mybatis的深入学习(增删改查的操作)
上一篇我们的项目搭建好了,也写了简答的Junit测试类进行测试,可以正确映射到数据库中。
那么这篇文章来深入学习一下以下几个点:
- 了解MyBatis的核心对象SqlSessionFactoryBuilder以及它的作用
- 掌握MyBatis核心配置文件以及元素的使用。
- 掌握MyBatis映射文件及其元素的使用。
一、什么是MyBatis的核心对象?
可以看到红色框住的这部分代码,我们使用了SqlSessionFactoryBuilder().build(reader)来创建MyBatis的SqlSessionFactory的一个实例。
稍微解释一下代码吧(这是一种链式操作,使得代码更为紧凑方便阅读):
SqlSessionFactoryBuilder()
:这是 MyBatis 框架提供的 SqlSessionFactoryBuilder 类的构造方法,用于创建 SqlSessionFactory 实例的构建器。build(reader)
:这是 SqlSessionFactoryBuilder 类 的build
方法 ,用于**构建 SqlSessionFactory 实例。**该方法需要一个 Reader 参数,该 Reader 包含了 MyBatis 配置文件的内容。通常,配置文件名为mybatis-config.xml
。
但是呢,写入数据库的操作是SqlSession对象完成的,所以我们上面创建了SqlSessionFactory的实例就是为了通过其中的build()方法创建出一个SqlSession对象,这样才能进行数据库操作。
java
//创建SqlSession实例
SqlSession session = sqlSessionFactory.openSession();
//调用方法,传入参数进行查询\插入\更新\删除等操作
PasswordMS passwordMS = session.selectOne("findById",1);//SqlSession中查询单个对象的方法,若是要查询多条则要使用selectList(),方法不同返回值也不同。
//日志输出信息查看返回结果
logger.info("姓名:"+passwordMS.getAccount()+",密码:"+passwordMS.getPassword()+",网站:"+passwordMS.getWebsiteName());
//关闭session
session.close();
因此SqlSessionFactoryBuilder通常被认为是Mybatis的核心对象!(若是上面的代码解释,还是不太懂的话,建议去看看我的其他俩个专栏,先把Java基础和Java高级编程学习这俩专栏学习看完,或者也可以单独看一下Java方法详解这一篇文章,即可理解大概的逻辑思路。)
SqlSessionFactoryBuilder中有多个重载的build()方法
可以看出都是构建出SqlSessionFactory对象, 通过以上代码可知,配置信息可以通过InputStream(字节流)、Reader(字符流)、Configuration(类)三种形式提供给SqlSessionFactoryBuilder的build()方法。
我们是以读取XML文件的方式构造SqlSessionFactory对象
java
//字符流 读取配置文件
Reader reader = Resources.getResourceAsReader("配置文件位置");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(reader);
-----------------------------------------------------------------------------
//字节流 读取配置文件
InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
----------------------------------------------------------------------------
//类方式可以自己去尝试编写一下,开发中常用这种方式
SqlSessionFactory对象是线程安全的,它一旦被创建,在整个应用程序执行期间都会存在 。如果我们多次创建同一个数据库的SqlSessionFactory对象,那么该数据库的资源将很容易被耗尽。通常每一个数据库都只创建一个SqlSessionFactory对象,所以在构建SqlSessionFactory对象时,建议使用单例模式。
通过SqlSessionFactory的openSession()方法创建出SqlSession来操作数据库
方法名称 | 描述 |
---|---|
SqlSession openSession() | 开启一个事务。 |
SqlSession openSession(Boolean autoCommit) | 参数autoCommit可设置是否开启事务。 |
SqlSession openSession(Connection connection) | 参数connection可提供自定义连接。 |
SqlSession openSession(TransactionIsolationLevel level) | 参数level可设置隔离级别。 |
SqlSession openSession(ExecutorType execType) | 参数execType有三个可选值。 |
SqlSession openSession(ExecutorType execType,Boolean autoCommit) | 参数execType有三个可选值。 |
SqlSession openSession(ExecutorType execType, Connection connection) | 参数ExecutorType有三个可选值。 |
openSession(ExecutorType execType)简称execType参数值 有三个可选值:
- ExecutorType.SIMPLE:表示为每条语句创建一条新的预处理语句。
- ExecutorType.REUSE:表示会复用预处理语句。
- ExecutorType.BATCH:表示会批量执行所有更新语句。
简单理解,知道有这些方法设置就行后面才会用到。
SqlSession对象的作用
SqlSession 是MyBatis框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,主要作用是执行持久化操作 ,类似于JDBC中的Connection。SqlSession对象包含了执行SQL操作的方法,由于其底层封装了JDBC连接,所以可以直接使用SqlSession对象来执行 已映射的SQL语句。
以下是一些常见的 SqlSession
方法:
-
selectOne(String statement, Object parameter) : 执行查询并返回单个结果对象。
statement
是 SQL 语句的唯一标识符,parameter
是查询所需的参数。 -
selectList(String statement, Object parameter) : 执行查询并返回结果列表。与
selectOne
类似,只是返回多个结果。 -
insert(String statement, Object parameter): 执行插入操作,插入一条数据。
-
update(String statement, Object parameter): 执行更新操作,更新数据。
-
delete(String statement, Object parameter): 执行删除操作,删除数据。
-
commit(): 提交事务。
-
rollback(): 回滚事务。
-
close() : 关闭
SqlSession
实例。 -
getMapper(Class type): 获取一个 Mapper 接口的实例,通过该实例可以调用映射文件中配置的 SQL 语句。
示例使用:
java
SqlSession session = sqlSessionFactory.openSession();
// 查询单个结果
User user = session.selectOne("getUserById", 1);
// 查询结果列表
List<User> userList = session.selectList("getAllUsers");
// 插入数据
User newUser = new User("John", "john@example.com");
int rowsInserted = session.insert("insertUser", newUser);
// 更新数据
User updatedUser = new User(1, "UpdatedName", "updated@example.com");
int rowsUpdated = session.update("updateUser", updatedUser);
// 删除数据
int rowsDeleted = session.delete("deleteUser", 1);
// 提交事务
session.commit();
// 关闭 SqlSession
session.close();
上述示例中的方法参数 "getUserById"
、"getAllUsers"
、"insertUser"
、"updateUser"
、"deleteUser"
是 MyBatis 配置文件中定义的 SQL 语句的唯一标识符。这些标识符与映射文件中的配置相对应,可以在映射文件中查找具体的 SQL 语句。
PS(名词科普):提交事务
在数据库中,事务(Transaction)是指一系列的数据库操作 ,这些操作被当作一个单独的工作单元来执行。事务的目的是确保数据库的一组操作要么全部执行成功,要么全部失败,以保持数据的一致性和完整性。
提交事务(Commit Transaction)是指将之前在一个事务中进行的一系列数据库操作永久地保存到数据库中,使这些操作对其他事务可见。当你执行提交事务操作时,数据库会将所有的变更持久保存,而且这些变更对其他事务和查询都是可见的。如果事务中的所有操作都执行成功,那么提交事务会将这些操作永久保存到数据库中。如果事务中的任何一个操作失败,那么整个事务都会被回滚(Rollback),即取消之前的操作,使数据库回到事务开始前的状态。
提交事务是数据库管理系统中确保数据一致性和持久性的重要机制之一,它确保了在事务执行完毕后,对数据的变更是持久保存的,而不会因为系统崩溃等情况而丢失。
SqlSession对象的使用范围:
每一个线程都应该有一个自己的SqlSession对象,并且该对象不能共享 。SqlSession对象是线程不安全的 ,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围(如Servlet的HttpSession)中使用。SqlSession对象使用完之后,要及时的关闭,SqlSession对象通常放在finally块中关闭,代码如下所示。
java
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 此处执行持久化操作
} finally { sqlSession.close(); }
二、MyBatis核心配置文件
其中的主要元素如下:
以下是一些核心配置文件和配置项的含义(""内的要重点掌握一下):
- properties: 用于指定一些全局的属性和变量,这些属性可以在整个配置文件中使用。
- "settings": 用于配置 MyBatis 的全局性设置,例如缓存、延迟加载等。
- "typeAliases": 用于配置类型别名,使得在映射文件中可以使用简短的别名来引用 Java 类。
- typeHandlers: 用于配置类型处理器,用于将数据库中的数据类型映射为 Java 类型。
- objectFactory: 用于配置对象工厂,可以通过对象工厂来创建结果对象的实例。
- plugins: 用于配置插件,插件可以拦截 MyBatis 的一些操作,扩展其功能。
- environments: 用于配置不同的运行环境,比如开发环境、测试环境和生产环境。
- environment : 在
environments
中配置的运行环境,可以指定transactionManager
和dataSource
。 - transactionManager: 用于配置事务管理器,管理数据库连接的生命周期和事务的提交与回滚。
- dataSource: 用于配置数据源,包括数据库连接的基本信息。
- mappers: 用于指定映射器接口的位置,即映射文件的位置。
元素是整个XML配置文件的根元素,相当于MyBatis各元素的管理员。有很多子元素,MyBatis的核心配置就是通过这些子元素完成的,子元素的顺序尽量按照上述的编号顺序,并且需要在 标签对中即可。
这里重点讲一下****的配置元素:
配置参数 | 描述 |
---|---|
cacheEnabled | 用于配置是否开启缓存。 |
lazyLoadingEnabled | 延迟加载的全局开关。 |
aggressiveLazyLoading | 关联对象属性的延迟加载开关。 |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 |
useColumnLabel | 使用列标签代替列名。 |
useGeneratedKeys | 允许JDBC支持自动生成主键,需要驱动兼容。 |
autoMappingBehavior | 指定MyBatis应如何自动映射列到字段或属性。 |
defaultExecutorType | 配置默认的执行器。 |
defaultStatementTimeout | 配置超时时间,它决定驱动等待数据库响应的秒数。 |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射。 |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。 |
格式如下:
xml
<settings>
<!-- 是否开启缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 是否开启延迟加载,如果开启,所有关联对象都会延迟加载 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 是否开启关联对象属性的延迟加载,如果开启,对任意延迟属性的调用都
会使用带有延迟加载属性的对象向完整加载,否则每种属性都按需加载 -->
<setting name="aggressiveLazyLoading" value="true" />
...
</settings>
这里重点讲一下typeAiases元素:
核心配置文件若要引用一个POJO实体类,需要输入POJO实体类的全限定类名,而全限定类名比较冗长,如果直接输入,很容易拼写错误。例如,POJO实体类User的全限定类名是com.itheima.pojo.User,未设置别名之前,映射文件的select语句块若要引用POJO类User,必须使用其全限定类名,引用代码如下。
xml
<select id="findById" parameterType="int" resultType="com.example.pojo.User">
select * from users where id = #{id}
</select>
设置别名的方式如下:
①在元素下,使用多个元素为每一个全限定类逐个配置别名。
xml
<typeAliases>
<typeAlias alias="User" type="com.example.pojo.User"/>
<typeAlias alias="Student" type="com.example.pojo.Student"/>
<typeAlias alias="Employee" type="com.example.pojo.Employee"/>
<typeAlias alias="Animal" type="com.example.pojo.Animal"/>
</typeAliases>
<environments>
②通过自动扫描包的形式自定义别名。
xml
<typeAliases>
<package name="com.example.pojo"/>
</typeAliases>
<environments>
注意要放在 environments上面,不然程序会报错,Error building SqlSession。
除了可以使用typeAliases元素为实体类自定义别名外,MyBatis框架还为许多常见的Java类型(如数值、字符串、日期和集合等)提供了相应的默认别名。例如别名_byte映射类型byte、_long映射类型long等,别名可以在MyBatis中直接使用,但由于别名不区分大小写,所以在使用时要注意重复定义的覆盖问题。
并且设置完别名以后,pojo包里面的类都会自动识别,所以Mapper.xml文件里面也需要修改一下。如下图得从pojo.PasswordMS 修改➡成 PasswordMS:
三、MyBatis映射文件及其元素的使用
上一篇文章,我们后面自己写一个插入的sql语句,现在要检测一下能不能正确的映射到数据库中。
结果发现,程序一直转动,但是不能在终端输入,也没办法继续进行。神奇的是也不报错,在我查阅资料后发现,原因如下
在JUnit测试中,无法直接通过终端输入参数。JUnit测试是自动化测试,在测试过程中是无法进行交互操作的。
解决办法:pom中引入Mockito,就可以模拟用户输入了
java
<!-- Mockito 依赖 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
测试类也要修改
java
lass PasswordMSTest {
private Logger logger= Logger.getLogger(PasswordMSTest.class);
private InputStream originalSystemIn;
private ByteArrayInputStream simulatedInput;
@BeforeEach
public void setUp() {
originalSystemIn = System.in;
}
@AfterEach
public void tearDown() {
System.setIn(originalSystemIn);
}
@org.junit.jupiter.api.Test
void insertOneIntoPasswordMS() {
//读取文件名:
String resources="mybatis-config.xml";
//创建流
Reader reader = null;
try{
reader = Resources.getResourceAsReader(resources);
}catch (IOException e){
e.printStackTrace();
}
//这里就是你模拟输入用户输入的数据
String input = "John\n123456\n1234567890\nexample1.com\nhttps://example1.com\nicon.png\nSocial\n";
// 将标准输入重定向到模拟输入流
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
System.setIn(inputStream);
PasswordMS passwordMS=new PasswordMS();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你想要存储的账号名:");
passwordMS.setAccount(scanner.nextLine());
System.out.println("请输入你想要存储的账号密码:");
passwordMS.setPassword(scanner.nextLine());
System.out.println("请输入你想要存储的与该账号绑定的手机号:");
passwordMS.setPhoneNumber(scanner.nextLine());
System.out.println("请输入你想要存储的账号所属网站名:");
passwordMS.setWebsiteName(scanner.nextLine());
System.out.println("请输入你想要存储的网址URL:");
passwordMS.setWebsiteURL(scanner.nextLine());
System.out.println("请输入网站的缩略图或者图标:");
String WebsiteImage=scanner.nextLine();
Optional<String> optionalS = Optional.ofNullable(WebsiteImage);
passwordMS.setWebsiteImage(optionalS.orElse("Sorry 还没有"));//这里使用了optional对象来判断是否为空,空则为默认值
System.out.println("请输入该账号的描述比如类别:");
passwordMS.setAccountDescription(scanner.nextLine());
//初始化mybatis数据库,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建SqlSession实例
SqlSession session = sqlSessionFactory.openSession();
//传入参数查询,返回结果
session.insert("insertOne",passwordMS);
session.commit();
session.close();
}
}
输出结果如下:
数据库中也成功插入记录
现在回过头来看,每个测试类其实有很大一部分都是重复的
这段代码的作用是打开mybatis-config.xml文件,用它连上数据库,然后打开数据连接,这段代码经常会在进行数据操作之前用到,但是我们又不想每次都复制粘贴它,这时我们可以把它封装起来。直接调用。
操作如下:
①java包下新建一个package为utils(一般默认为工具包)
②utils包下新建一个类为:MyBatisUtil.java
代码如下:
java
package utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtil {
private static SqlSessionFactory factory;
static{在静态代码块中,factory只会被创建一次
System.out.println("static factory===============");
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SqlSession createSqlSession(){
return factory.openSession(false);//true为自动提交事务
}
public static void closeSqlSession(SqlSession sqlSession){
if(null != sqlSession)
sqlSession.close();
}
}
通过以上"静态类"的方式来保证Sql Session Factory实例只被创建一次,当然,最佳的解决方案是使用Spring框架来管理Sql Session Factory的单例模式生命周期。关于和Spring的集成,我们会在后面使用到中进行讲解。
完成以上步骤以后,我们就能去优化我们的测试类了
进行测试,还是能够正常运行,也能写入数据库进行交互。
使用Mybatis进行数据修改、删除操作
自行编写补充mapper映射文件嗷
总结
这是第二天对SSM框架的学习,深入了解了Mybatis的核心对象SqlSessionFactoryBuilder ,掌握MyBatis核心配置文件以及元素的使用,也掌握MyBatis映射文件及其元素的使用。想要跟着学习的可以去我的资源里面找对应的文件下载,我的md文件也会发上去,项目文件会上传可以自己跟着学习一下。
PS:sql语句自己编写┗|`O′|┛ 嗷~~
作者:Stevedash
发表于:2023年8月21日 22点45分