1.SpringBoot 持久化支持(MyBatis)
1.1 XML版本
1.1.1 步骤分析
- 新建maven project;
- 在pom.xml中引入相关依赖
- (1)基本依赖,jdk版本号;
- (2)mysql驱动,mybatis依赖包,mysql分页PageHelper:
- 创建启动类App.java
- 在application.properties添加配置文件;
- 创建数据库和表
- 编写domain的测试类User;
- 编写UserMapper;
- 编写mybatis的xml文件
- 编写IUserService接口和接口的实现类UserServiceImpl
- 编写UserController
- 编写测试方法,测试service层
- 启动项目,测试controller层
- 配置事务。注解式事务
- 测试事务
- 获取自增的主键id
- 使用插件PageHelper分页
1.1.2 实现
- 新建maven project;
- 在pom.xml中引入相关依赖
- (1)基本依赖,jdk版本号
- (2)mysql驱动,mybatis依赖包,mysql分页PageHelper
<!--子模块中配置导包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- mysql 数据库驱动. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--
spring-boot mybatis依赖:
请不要使用1.0.0版本,因为还不支持拦截器插件,
1.1.1 是博主写帖子时候的版本,大家使用最新版本即可
-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!--
MyBatis提供了拦截器接口,我们可以实现自己的拦截器,
将其作为一个plugin装入到SqlSessionFactory中。
Github上有位开发者写了一个分页插件,我觉得使用起来还可以,挺方便的。
Github项目地址: https://github.com/pagehelper/Mybatis-PageHelper
-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.0</version>
</dependency>
- 创建启动类App.java
package cn.wangningbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("cn.wangningbo.mapper")//注意这个注解,使用mybatis扫描包的mapper包
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
- 在resources下创建配置文件application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
driver-class-name: com.mysql.jdbc.Driver
username: root
password: apy06942
mybatis:
type-aliases-package: cn.wangningbo.domain,cn.wangningbo.query
- 创建数据库和表
- 编写domain的测试类User;
package cn.wangningbo.domain;
public class User {
private Long id;
private String name;
//提供get方法、set方法、无参构造方法和一个name参数的构造方法、toString方法
}
- 编写UserMapper;
package cn.wangningbo.mapper;
import cn.wangningbo.domain.User;
import java.util.List;
public interface UserMapper {
void save(User user);
List<User> loadAll();
}
- 编写mybatis的xml文件
在resources下创建目录cn/wangningbo/mapper,在此目录下创建相应的映射xml,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="cn.wangningbo.mapper.UserMapper">
<insert id="save" parameterType="cn.wangningbo.domain.User">
INSERT into t_user (name) values (#{name})
</insert>
<select id="loadAll" resultType="User">
SELECT * from t_user
</select>
</mapper>
- 编写IUserService接口和接口的实现类UserServiceImpl
package cn.wangningbo.service;
import cn.wangningbo.domain.User;
import java.util.List;
public interface IUserService {
void add(User user);
List<User> getAll();
}
package cn.wangningbo.service.impl;
import cn.wangningbo.domain.User;
import cn.wangningbo.mapper.UserMapper;
import cn.wangningbo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
public void add(User user) {
userMapper.save(user);
}
public List<User> getAll() {
return userMapper.loadAll();
}
}
- 编写UserController
package cn.wangningbo.controller;
import cn.wangningbo.domain.User;
import cn.wangningbo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
//保存由测试做就行,这里就不写页面了
//查询所有
@GetMapping
public List<User> getAll() {
return userService.getAll();
}
}
- 编写测试方法,测试service层
package cn.wangningbo.service;
import cn.wangningbo.App;
import cn.wangningbo.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class IUserServiceTest {
@Autowired
private IUserService userService;
@Test
public void add() {
userService.add(new User("二狗"));
userService.add(new User("张三"));
}
@Test
public void getAll() {
List<User> list = userService.getAll();
for (User user : list) {
System.out.println(user);
}
}
}
- 启动项目,测试controller层
http://localhost:8080/user
- 配置事务。注解式事务
在service层加入事务
//类级别值只读事务
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
//在类里面写方法上面打写事务,下面这两种的效果是一样的
//@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@Transactional
package cn.wangningbo.service.impl;
import cn.wangningbo.domain.User;
import cn.wangningbo.mapper.UserMapper;
import cn.wangningbo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
//类级别值只读,在类里面写方法上面打写事务
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
//@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@Transactional
public void add(User user) {
userMapper.save(user);
// int i = 1/0;
}
public List<User> getAll() {
return userMapper.loadAll();
}
}
- 测试事务
由于在service那里的add方法弄了一个int i = 1/0;事务成功了就不会添加到数据库,注释掉那一行 int i = 1/0;就会添加到数据库。
- 获取自增的主键id
只需要在Mapper.xml里面的添加方法配置一下useGeneratedKeys="true" keyProperty="id" keyColumn="id"
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="cn.wangningbo.mapper.UserMapper">
<insert id="save" parameterType="cn.wangningbo.domain.User"
useGeneratedKeys="true"
keyProperty="id"
keyColumn="id">
INSERT into t_user (name) values (#{name})
</insert>
<select id="loadAll" resultType="User">
SELECT * from t_user
</select>
</mapper>
测试是否获得主键id,执行测试方法输出查看是否有id
@Test
public void add() {
User user = new User("二狗");
System.out.println(user.getId());
userService.add(user);
System.out.println(user.getId());
}
输出结果:
null
6
- 使用插件PageHelper分页
PageHelper是mybatis一个插件,可以用它完成分页
步骤:导入jaar,在配置类里面配置Bean,测试
导入jar
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.0</version>
</dependency>
配置
package cn.wangningbo.config;
import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class MyConfig {
@Bean
public PageHelper pageHelper() {
System.out.println("MyBatisConfiguration.pageHelper()");
PageHelper pageHelper = new PageHelper();
Properties p = new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
pageHelper.setProperties(p);
return pageHelper;
}
}
service层进行分页
public List<User> getAll() {
PageHelper.startPage(1, 3);
return userMapper.loadAll();
}
测试
@Test
public void getAll() {
//此时list已经不是传统list,相当于原来pageList
Page<User> list = (Page<User>) userService.getAll();
//class com.github.pagehelper.Page
System.out.println(list.getClass());
//还有分页信息
System.out.println(list.getTotal());
System.out.println(list.getPageNum());
//获取数据:本身也是一个继承与ArraList的List
for (User user : userService.getAll()) {
System.out.println(user);
}
}
输出结果
class com.github.pagehelper.Page
5
1
User{id=1, name='二狗'}
User{id=2, name='张三'}
User{id=4, name='二狗'}
1.2 注解版
注解版与xml版很相似,我只简单说几句不一样的地方
- 注解版使用mybatis的时候,只需要在Mapper接口那里的方法头上打注解即可,以前使用xml版本的时候都是在xml里面写的查询,添加等方法!
package cn.wangningbo.mapper;
import cn.wangningbo.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@Insert("insert into t_user(name) values (#{name})")
void save(User user);
@Select("select * from t_user")
List<User> loadAll();
}
2. SpringBoot启动分析
2.1 创建SpringApplication对象
initialize(sources);
private void initialize(Object[] sources) {
//保存主配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断当前是否一个web应用
this.webEnvironment = deduceWebEnvironment();
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2 运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
Banner printedBanner = printBanner(environment);
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
//
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}