上一篇我们的项目搭建好了,也写了简答的Junit测试类进行测试,可以正确映射到数据库中。
那么这篇文章来深入学习一下以下几个点:
可以看到红色框住的这部分代码,我们使用了SqlSessionFactoryBuilder().build(reader)来创建MyBatis的SqlSessionFactory的一个实例。
稍微解释一下代码吧(这是一种链式操作,使得代码更为紧凑方便阅读):
SqlSessionFactoryBuilder()
:这是 MyBatis 框架提供的 SqlSessionFactoryBuilder 类的构造方法,用于创建 SqlSessionFactory 实例的构建器。build(reader)
:这是 SqlSessionFactoryBuilder 类的 build
方法,用于**构建 SqlSessionFactory 实例。**该方法需要一个 Reader 参数,该 Reader 包含了 MyBatis 配置文件的内容。通常,配置文件名为 mybatis-config.xml
。 但是呢,写入数据库的操作是SqlSession对象完成的,所以我们上面创建了SqlSessionFactory的实例就是为了通过其中的build()方法创建出一个SqlSession对象,这样才能进行数据库操作。
//创建SqlSession实例
SqlSession session = sqlSessionFactory.openSession();
//调用方法,传入参数进行查询\插入\更新\删除等操作
PasswordMS passwordMS = session.selectOne("findById",1);//SqlSession中查询单个对象的方法,若是要查询多条则要使用selectList(),方法不同返回值也不同。
//日志输出信息查看返回结果
logger.info("姓名:"+passwordMS.getAccount()+",密码:"+passwordMS.getPassword()+",网站:"+passwordMS.getWebsiteName());
//关闭session
session.close();
可以看出都是构建出SqlSessionFactory对象, 通过以上代码可知,配置信息可以通过InputStream(字节流)、Reader(字符流)、Configuration(类)三种形式提供给SqlSessionFactoryBuilder的build()方法。
我们是以读取XML文件的方式构造SqlSessionFactory对象
//字符流 读取配置文件
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对象时,建议使用单例模式。
方法名称 | 描述 |
---|---|
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有三个可选值。 |
简单理解,知道有这些方法设置就行后面才会用到。
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 语句。
示例使用:
SqlSession session = sqlSessionFactory.openSession();
// 查询单个结果
User user = session.selectOne("getUserById", 1);
// 查询结果列表
List<User> userList = session.selectList("getAllUsers");
// 插入数据
User newUser = new User("John", "[email protected]");
int rowsInserted = session.insert("insertUser", newUser);
// 更新数据
User updatedUser = new User(1, "UpdatedName", "[email protected]");
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 语句。
在数据库中,事务(Transaction)是指一系列的数据库操作,这些操作被当作一个单独的工作单元来执行。事务的目的是确保数据库的一组操作要么全部执行成功,要么全部失败,以保持数据的一致性和完整性。
提交事务(Commit Transaction)是指将之前在一个事务中进行的一系列数据库操作永久地保存到数据库中,使这些操作对其他事务可见。当你执行提交事务操作时,数据库会将所有的变更持久保存,而且这些变更对其他事务和查询都是可见的。如果事务中的所有操作都执行成功,那么提交事务会将这些操作永久保存到数据库中。如果事务中的任何一个操作失败,那么整个事务都会被回滚(Rollback),即取消之前的操作,使数据库回到事务开始前的状态。
提交事务是数据库管理系统中确保数据一致性和持久性的重要机制之一,它确保了在事务执行完毕后,对数据的变更是持久保存的,而不会因为系统崩溃等情况而丢失。
每一个线程都应该有一个自己的SqlSession对象,并且该对象不能共享。SqlSession对象是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围(如Servlet的HttpSession)中使用。SqlSession对象使用完之后,要及时的关闭,SqlSession对象通常放在finally块中关闭,代码如下所示。
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 此处执行持久化操作
} finally { sqlSession.close(); }
其中的主要元素如下:
environments
中配置的运行环境,可以指定 transactionManager
和 dataSource
。 元素是整个XML配置文件的根元素,相当于MyBatis各元素的管理员。有很多子元素,MyBatis的核心配置就是通过这些子元素完成的,子元素的顺序尽量按照上述的编号顺序,并且需要在 标签对中即可。
配置参数 | 描述 |
---|---|
cacheEnabled | 用于配置是否开启缓存。 |
lazyLoadingEnabled | 延迟加载的全局开关。 |
aggressiveLazyLoading | 关联对象属性的延迟加载开关。 |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 |
useColumnLabel | 使用列标签代替列名。 |
useGeneratedKeys | 允许JDBC支持自动生成主键,需要驱动兼容。 |
autoMappingBehavior | 指定MyBatis应如何自动映射列到字段或属性。 |
defaultExecutorType | 配置默认的执行器。 |
defaultStatementTimeout | 配置超时时间,它决定驱动等待数据库响应的秒数。 |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射。 |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。 |
格式如下:
<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,必须使用其全限定类名,引用代码如下。
<select id="findById" parameterType="int" resultType="com.example.pojo.User">
select * from users where id = #{id}
select>
<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>
<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:
上一篇文章,我们后面自己写一个插入的sql语句,现在要检测一下能不能正确的映射到数据库中。
结果发现,程序一直转动,但是不能在终端输入,也没办法继续进行。神奇的是也不报错,在我查阅资料后发现,原因如下
在JUnit测试中,无法直接通过终端输入参数。JUnit测试是自动化测试,在测试过程中是无法进行交互操作的。
<!-- Mockito 依赖 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
测试类也要修改
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
代码如下:
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分