SpringBoot框架
内容管理
SpringBoot框架整合持久层框架,Mybatis
前面已经分享了SpringBoot的基本用法:建立SpringBoot项目,使用配置类的方式注册Servlet、Filter,使用application.yml注册相关配置项;接下来就是将Mybaits整合到SpringBoot中
最近有很多事情,CET-6刷分,软考,课程考试,准备八股…SpringBoot只能抱歉达到最简了
ORM操作MySQL
object relative mapping对象关系映射;Mybatis框架就是ORM的一种;这里主要分析通过Mybatis框架,读写MySQL数据
这里没有什么多difficult的地方,所以就直接上操纵,使用Windows+ R打开cmd窗口,打开mysql工具,在cfengbase数据库下面建立一张表student;表中3个字段name,id,class
mysql> DESC student;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| stuno | int | NO | PRI | NULL | auto_increment |
| stuname | varchar(255) | YES | | NULL | |
| stuclass | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
SpringBoot集成Mybaits步骤
- 添加mybaits起步依赖:完成mybatis对象的自动配置、对象放在容器中【mybatis有自动配置的直接,会将mybatis的相关对象放到容器中】
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybaits起步依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
mybaits-starter默认使用的连接池是com.zaxxer的hikariCP; 这个连接池比其他的连接池速度更快; 起步依赖加入了mybatis和mybatis-spring、spring-jdbc等依赖;不需要再像SSM一样再加入
- 在pom.xml指定资源插件,将src/main/java目录的xml文件读取移动到classPath中
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<!-- 还要配置上原来编译资源的路径-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
- 创建实体类Student,这里easy
package cshen.model;
public class Student {
private int stuno;
private String stuname;
private String stuclass;
public Student() {
}
public Student(int stuno, String stuname, String stuclass) {
this.stuno = stuno;
this.stuname = stuname;
this.stuclass = stuclass;
}
public int getStuno() {
return stuno;
}
public void setStuno(int stuno) {
this.stuno = stuno;
}
public String getStuname() {
return stuname;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public String getStuclass() {
return stuclass;
}
public void setStuclass(String stuclass) {
this.stuclass = stuclass;
}
@Override
public String toString() {
return getClass().getName().toString() + "{" + //this都可以省略不写
"stuno=" + stuno +
", stuname='" + stuname + '\'' +
", stuclass='" + stuclass + '\'' +
'}';
}
}
这里重写toString方法的时候建议使用反射机制来获取类名,开闭原则
- 创建Dao接口,创建方法,并在mapper文件中书写SQL语句
package cshen.dao;
import cshen.model.Student;
import org.apache.ibatis.annotations.Mapper;
/**
* 告诉mybatis框架,为这个接口创建代理对象
*/
@Mapper
public interface StudentDao {
public Student selectByName(String stuname);
}
<mapper namespace="cshen.dao.StudentDao">
<select id="selectByName" resultType="cshen.model.Student">
SELECT stuno,stuname,stuclass FROM student WHERE stuname = #{stuname}
</select>
</mapper>
- 创建Service和Controller层的对象
package cshen.service.impl;
import cshen.dao.StudentDao;
import cshen.model.Student;
import cshen.service.StudentService;
import javax.annotation.Resource;
@Service //生成单例对象放入容器
public class StudentServiceImpl implements StudentService {
//动态代理生成了Dao对象放在容器中,直接取出注入
@Resource
private StudentDao studentDao;
@Override
public Student queryStudent(String stuName) {
return studentDao.selectByName(stuName);
}
}
这里只是为了演示SpringBoot整合Mybaits,业务逻辑是不可能如此简单的
package cshen.controller;
import cshen.model.Student;
import cshen.service.StudentService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
@Controller
public class StudentController {
@Resource
private StudentService studentService;
@RequestMapping("/student/query")
@ResponseBody
public Student searchStudent(String stuName) {
return studentService.queryStudent(stuName);
}
}
- 书写application.properties文件【数据库的连接信息】
###配置端口号
server:
port: 8081
servlet:
context-path: /bootOrm
encoding: #全局过滤器
charset: UTF-8
force: true
###配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cfengbase?serverTimezone=GMT%2B8
username: cfeng
password: a1234567890b
SpringBoot欢迎页面【默认首页】,如果不手动配置,那么系统默认static包下面的index.html为首页;如果想自定义可以 1. 通过controller中进行转发;Mapping为/; 2、编写webMVC的配置类,设置addViewController; 使用ViewControllerRegistory来进行注册
这样就成功完成了数据库的连接操作,与SSM相比简化了许多许多,最主要的就是再次封装,不用手动创建SqlSessionFactry等对象,还有就是mapper的扫描器就一个注解Mapper代替了😳
第一种方式: @Mapper注解
@Mapper: 放在dao接口的上面,每一个接口都需要使用这个注解
----- 可以告诉mybaits这是dao接口,让其创建该接口的代理对象放入容器中供service使用
@Mapper
public interface StudentDao {
public Student selectByName(String stuname);
}
但是这个注解的问题就是需要在每一个接口的上方都需要写,一旦操作的表的接口过多,就非常繁琐
第二种方式: @MapperScan
使用Mapper注解不方便,所以推荐使用@MapperScan;这个注解放在主类--- 也就是起始类【加SpringBootApplicaton注解】上方
; 其参数就是Dao接口所在的包
package cshen;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("cshen.dao") //这里是一个数组
public class BootApplication4 {
public static void main(String[] args) {
//执行操作返回的是容器对象;可以获取容器;并且使用ApplicationRunner接口操作
SpringApplication.run(BootApplication4.class, args);
}
}
查询之后正常使用
INFO 13468 --- [nio-8081-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2022-03-30 20:19:36.443 INFO 13468 --- [nio-8081-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
可以看到hikari数据源成功进行了操作
这里的Mapper和MapperScan注解在之前的SSM中就是
就是这个MapperScannerConfiger扫描包,然后创建代理对象以供使用
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 相当于内部调用这个方法,所以需要factory对象,还需要接口的类型 看到是String类型的,使用value,不是ref-->
<property name="sqlSessionFactoryBeanName" value="mySqlSession"/>
<!-- 指定包名,包名是dao接口所在的包名,MapperScannerConfig会扫描这个包中所有的接口,
都调用getMapper方法为每一个接口代理一个dao对象,创建好的所有的dao对象存放在容器中
-->
<property name="basePackage" value="cfeng.dao"/>
</bean>
dao和xml文件分开 — yml中配置
之前的项目中,mapper文件和dao接口总是放在一起的,这不利于管理,因为java目录下应该都是java文件,xml文件应该放到Resource下面去
放到自定义的/resource/mapper文件夹下面,这时就出现了新的问题: 系统找不到mapper文件位置;所以需要在application中进行配置
##mapper文件分开管理--指定位置
mybatis:
mapper-locations: classpath:mapper/*.xml #mapper下面的所有的xml文件
需要注意像博主一样加入资源插件,不然又找不到Resource下面的静态资源了:happy:
这里之前没有配置日志,可以再进行配置【这里的mybatis下面的选项都是之前的Mybatis的主配置文件中的配置的选项】
mybatis:
mapper-locations: classpath:mapper/*.xml #mapper下面的所有的xml文件
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #输出到控制台
这样再执行一次
JDBC Connection [HikariProxyConnection@923951926 wrapping com.mysql.cj.jdbc.ConnectionImpl@45e81348] will not be managed by Spring
==> Preparing: SELECT stuno,stuname,stuclass FROM student WHERE stuname = ?
==> Parameters: 王6(String)
<== Columns: stuno, stuname, stuclass
<== Row: 2, 王6, HC2004
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7ec187c7]
可以看到就开启了日志,记录输出到控制台
现在就可以不需要上面插件中的java目录的编译了;因为java下面已经没有静态资源了;只用使用Resource即可【之前SSM中也可以进行指定,当时忘记说了,抱歉】
事务tx
Spring框架中使用事务: 使用aspectJ框架实现配置文件中实现事务;还有就是依赖@Transaction注解
当时需要再spring配置文件中指定一个事务管理其对象【事务管理器有很多种接口,不同的数据库有不同的类型,mybaits访问使用的就是DataSourceTransactionManager】
<!-- 声明事务管理器对象 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 要说明连接的数据库的信息,知道为哪张表开启事务 -->
<property name="dataSource" ref="myDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
加上注解驱动,就可以使用@Transactional方式实现事务,在参数中指定隔离级别和传播行为;AOP的实现也是动态代理,拿到原对象进行功能增强
在配置文件中说明声明式事务: 控制事务: 隔离级别,传播行为,超时时间
SpringBoot使用事务
SpringBoot只是对于Spring和SpringMVC进行简化,所以上面Spring的两种方式,都是可以使用的
业务方法加入@Transactional; 同时主启动类加上@TransactionManager
直接在业务方法上加上注解即可;如果不进行指定,那么就是数据库的默认隔离级别,传播行为是REQUIRED;在默认情况下,事务时默认自动提交的,
这里可以迅速新建一个插入学生的方法,在mapper文件中直接通过对象传参,这里就可以直接取得对象的同名参数【这和前面的Controller中自动取用返回的参数赋值给同名属性是相同的】
<insert id="insertStudent">
INSERT INTO student (stuno,stuname,stuclass) VALUES (#{stuno},#{stuname},#{stuclass})
</insert>
这里的SQL语句后面加上括号,不然SQL语句就是错误的
@Override
public int addStudent(Student student) {
int result = studentDao.insertStudent(student);
System.out.println("执行了业务方法");
//这里模拟抛出运行时异常
int m = 10/0; //算数异常; 如果是开发中按理就要回滚; 但是目前没有加事务,上面就会自动提交事务
return result;
}
那么接下来就使用注解来开启事务
@Override
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
public int addStudent(Student student) {
int result = studentDao.insertStudent(student);
System.out.println("执行了业务方法");
//这里模拟抛出运行时异常
// int m = 10/0; //算数异常; 如果是开发中按理就要回滚; 但是目前没有加事务,上面就会自动提交事务
return result;
}
运行后,控制台抛出异常,java.lang.ArithmeticException: / by zero;查看数据库,数据没有添加成功
还有配置文件使用的方式都还是支持的,可以去看博主之前的Spring的事务的部分🌳