spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=dbuser
spring.datasource.password=dbpass
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware,
InitializingBean {
}
spring.datasource.driver-class-name
为驱动类的全限定名称。schema.sql
CREATE TABLE `article`
(
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` int(11) NOT NULL COMMENT '作者 ID',
`title` varchar(100) NOT NULL COMMENT '文章标题',
`summary` varchar(200) DEFAULT NULL COMMENT '文章概要',
`read_count` int(11) unsigned zerofill NOT NULL COMMENT '阅读读数',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后修改时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4;
data.sql
INSERT INTO `article` VALUES ('1','2101','SpringBoot 核心注解',
'核心注解的主要作用','00000008976','2023-01-16 12:11:12','2023-01-16 12:11:19');
INSERT INTO `article` VALUES ('2','356752','JVM 调优',
'HotSpot 虚拟机详解','00000000026','2023-01-16 12:15:27','2023-01-16 12:15:30');
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
1.将 schema.sql , data.sql 拷贝到 resources 目录
2.修改application.properties
#配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=030522
#设置执行数据库的脚本
spring.sql.init.mode=never
3.创建实体类
@Data//set、get方法
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
public class ArticlePO {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
4.单元测试,注入JdbcTemplate对象,测试聚合函数
@SpringBootTest
class Springboot09JdbcTemplateApplicationTests {
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void test01() {
String sql="select count(*) as ct from article";
Long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println("记录行数 = " + count);
}
}
测试“?”占位符
@Test
void test02() {
//?作为占位符
String sql="select * from article where id=?";
//BeanPropertyRowMapper 将查询结果集、列名与属性名称匹配,名称完全匹配或驼峰
ArticlePO articlePO = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(ArticlePO.class), 1);
System.out.println("articlePO = " + articlePO);
}
测试自定义RowMapper
@Test
void testRowMapper() {
//只能查询出一个记录,查询不出记录抛出异常
String sql = "select * from article where id=1";
ArticlePO articlePO = jdbcTemplate.queryForObject(sql, (rs, rownum) -> {
var id = rs.getInt("id");
var userId = rs.getInt("user_id");
var title = rs.getString("title");
var summary = rs.getString("summary");
var readCount = rs.getInt("read_count");
var createTime = new Timestamp(rs.getTimestamp("create_time").getTime()).toLocalDateTime();
var updateTime = new Timestamp(rs.getTimestamp("update_time").getTime()).toLocalDateTime();
return new ArticlePO(id, userId, title, summary, readCount, createTime, updateTime);
});
System.out.println("查询的文章=" + articlePO);
}
测试List集合
@Test
void testList() {
String sql="select * from article order by id";
List<Map<String,Object>> listMap =jdbcTemplate.queryForList(sql);
listMap.forEach(el->{
el.forEach((field,value)->{
System.out.println("字段名称:"+field+",列值:"+value);
});
System.out.println("-------------------------------------");
});
}
测试更新记录
@Test
void testUpdate() {
String sql = "update article set title=? where id=?";
int update=jdbcTemplate.update(sql,"Java编程思想",2);
System.out.println("update = " + update);
}
示例
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Test
void testNameQuery() {
// :参数名
String sql = "select count(*) from article where user_id=:uid and read_count>:num";
//key是命名参数
Map<String, Object> param = new HashMap<>();
param.put("uid", 2101);
param.put("num", 1);
Long count = namedParameterJdbcTemplate.queryForObject(sql, param, Long.class);
System.out.println("count = " + count);
}
article_detail 表
CREATE TABLE `article_detail`
(
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '注解',
`article_id` int(11) NOT NULL COMMENT '文章 ID',
`content` text NOT NULL COMMENT '文章内容',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
1.创建实体类ArticleDetailPO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ArticleDetailPO {
private Integer id;
private Integer articleId;
private String content;
}
2.创建新的实体类ArticleMainPO,将ArticlePO作为成员变量
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ArticleMainPO {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
//一对一
private ArticleDetailPO detail;
}
3.查询
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Test
void testQueryContent() {
String sql = """
select m.*,d.id as detail_id,d.article_id,d.content
from article m left join article_detail d
on m.id=d.article_id
where m.id=:id
""";
Map<String, Object> param = new HashMap<>();
param.put("id", 1);
List<ArticleMainPO> mainList = namedParameterJdbcTemplate.query(sql, param, (rs, num) -> {
var id = rs.getInt("id");
var userId = rs.getInt("user_id");
var title = rs.getString("title");
var summary = rs.getString("summary");
var readCount = rs.getInt("read_count");
var createTime = new Timestamp(rs.getTimestamp("create_time").getTime()).toLocalDateTime();
var updatetime = new Timestamp(rs.getTimestamp("update_time").getTime()).toLocalDateTime();
//文章内容
var detailId = rs.getInt("detail_id");
var articleId = rs.getInt("article_id");
var content = rs.getString("content");
ArticleDetailPO articleDetailPO = new ArticleDetailPO(detailId, articleId, content);
return new ArticleMainPO(id, userId, title, summary, readCount, createTime, updatetime, articleDetailPO);
});
mainList.forEach(m -> {
System.out.println("m.getSummary() = " + m.getSummary());
System.out.println("m.toString() = " + m.toString());
System.out.println("m.getDetail() = " + m.getDetail());
});
}
数据库访问MyBatis、Mybatis-Plus在国内很常用,掌握了MyBatis、Mybatis-Plus就会大部分了。MyBatis-Plus附加的功能需要单独学习。我们以MyBatis来介绍SpringBoot集成ORM框架。
MyBatis使用最多的是mapper.xml文件编写SQL语句。本章使用MyBatis的注解,JDK新特性文本块,以及Record完成java对象和表数据的处理。
1.Maven依赖
<dependencies>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>3.0.0version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
2.创建实体类
@Data//set、get方法
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
public class ArticlePO {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
3.创建Mapper接口,实现CRUD操作
public interface ArticleMapper {
//查询结果ResultSet和PO对象的属性映射(关闭驼峰命名)
@Results(id = "BaseArticleMap", value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "user_id", property = "userId"),
@Result(column = "title", property = "title"),
@Result(column = "summary", property = "summary"),
@Result(column = "read_count", property = "readCount"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
//按主键查询
@Select("""
select id,user_id, title, summary, read_count, create_time, update_time
from article where id = #{articleId}
""")
ArticlePO selectById(@Param("articleId") Integer id);
//insert
@Insert("""
insert into article(user_id,title,summary,read_count,create_time,update_time)
values(#{userId},#{title},#{summary},#{readCount},#{createTime},#{updateTime})
""")
int insertArticle(ArticlePO po);
//update 使用参数名可以作为占位符 #{形参名}
@Update("""
update article set read_count=#{readCount} where id=#{id}
""")
int updateReadCount(Integer id, Integer readCount);
//delete
@Delete("""
delete from article where id = #{id}
""")
int deleteById(Integer id);
}
application.properties
#驼峰,下划线命名
mybatis.configuration.map-underscore-to-camel-case=true
4.启动类加入扫描注解
@MapperScan(basePackages = "com.hhb.mybatis.mapper")
@SpringBootApplication
public class Springboot10MyBatisApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot10MyBatisApplication.class, args);
}
}
5.配置数据源application.properties
#配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=030522
#配置MyBatis,支持驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
6.单元测试
@SpringBootTest
class MyBatisCRUDTest {
@Autowired
private ArticleMapper articleMapper;
@Test
void testSelect() {
ArticlePO articlePO = articleMapper.selectById(1);
System.out.println("articlePO.toString() = " + articlePO.toString());
}
@Test
void testInsert() {
ArticlePO articlePO = new ArticlePO();
articlePO.setTitle("TomcatWeb开发");
articlePO.setSummary("使用Tomcat服务器,定制web应用");
articlePO.setReadCount(19);
articlePO.setUserId(new Random().nextInt(500));
articlePO.setCreateTime(LocalDateTime.now());
articlePO.setUpdateTime(LocalDateTime.now());
int rows = articleMapper.insertArticle(articlePO);
System.out.println("rows = " + rows);
}
@Test
void testUpdate() {
int update = articleMapper.updateReadCount(3, 28);
System.out.println("update = " + update);
}
@Test
void testDelete() {
int update = articleMapper.deleteById(3);
System.out.println("update = " + update);
}
}
1.创建新的Mapper对象
public interface ArticleDao {
//查询某个用户的所有文章
@Select("""
select id,user_id,title,summary,read_count,create_time,update_time
from article where user_id=#{userId}
""")
@Results(id = "BaseArticleMap", value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "user_id", property = "userId"),
@Result(column = "read_count", property = "readCount"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
List<ArticlePO> selectList(Integer userId);
//根据id查询某个文章
@Select("""
select id,user_id,title,summary,read_count,create_time,update_time
from article where id=#{id}
""")
//引用定义好的结果映射,value的值是@Results中的id
@ResultMap("BaseArticleMap")
ArticlePO selectById(Integer id);
}
2.单元测试
@SpringBootTest
public class ResultMapTest {
@Resource
private ArticleDao articleDao;
@Test
void test01() {
List<ArticlePO> articlePOS = articleDao.selectList(480);
articlePOS.forEach(list ->
System.out.println(list));
}
@Test
void test02() {
ArticlePO articlePO = articleDao.selectById(4);
System.out.println("articlePO = " + articlePO);
}
}
1.ArticleDao.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hhb.mybatis.mapper.ArticleDao">
<resultMap id="ArticleBaseMapper" type="com.hhb.mybatis.po.ArticlePO">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="read_count" property="readCount"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
resultMap>
mapper>
2.修改application.properties配置mapper文件的路径
#指定自定义mapper文件的位置
mybatis.mapper-locations=classpath:/mapper/**/*.xml
3.修改ArticleDao的查询方法上面的@ResultMap
//根据id查询某个文章
@Select("""
select id,user_id,title,summary,read_count,create_time,update_time
from article where id=#{id}
""")
//引用定义好的结果映射,value的值是@Results中的id
//@ResultMap("BaseArticleMap")
//使用xml中的的id
@ResultMap("ArticleBaseMapper")
ArticlePO selectById(Integer id);
public static String selectById() {
return "SELECT * FROM users WHERE id = #{id}";
}
1.创建SQL提供者
public class SqlProvider {
//定义静态方法
public static String selectArticle() {
return "select * from article where id=#{id}";
}
public static String updateSql() {
return "update article set update_time=#{newTime} where id=#{id}";
}
public static String insertSql() {
return """
insert into article(user_id,title,summary,read_count,create_time,update_time)
values(#{userId},#{title},#{summary},#{readCount},#{createTime},#{updateTime})
""";
}
public static String deleteSql() {
return "delete from article where id=#{articleId}";
}
}
2.创建mapper接口
public interface ArticleRepository {
@Results(id = "NewBaseArticleMap", value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "user_id", property = "userId"),
@Result(column = "title", property = "title"),
@Result(column = "summary", property = "summary"),
@Result(column = "read_count", property = "readCount"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
//使用提供者
@SelectProvider(type = SqlProvider.class, method = "selectArticle")
ArticlePO selectByPrimary(Integer id);
@UpdateProvider(type = SqlProvider.class, method = "updateSql")
int updateTime(Integer id, LocalDateTime newTime);
@InsertProvider(type = SqlProvider.class, method = "insertSql")
int insertArticle(ArticlePO po);
@DeleteProvider(type = SqlProvider.class, method = "deleteSql")
int deleteArticle(Integer articleId);
}
3.单元测试
@SpringBootTest
public class RepositoryTest {
@Resource
private ArticleRepository articleRepository;
@Test
void testSelect() {
ArticlePO articlePO = articleRepository.selectByPrimary(4);
System.out.println("articleRepository = " + articleRepository);
}
@Test
void testUpdate() {
int update = articleRepository.updateTime(4, LocalDateTime.now());
System.out.println("update = " + update);
}
@Test
void testInsert() {
ArticlePO articlePO = new ArticlePO();
articlePO.setUserId(2345);
articlePO.setTitle("Spring6");
articlePO.setSummary("Spring6全新");
articlePO.setReadCount(6666);
articlePO.setCreateTime(LocalDateTime.now());
articlePO.setUpdateTime(LocalDateTime.now());
int update = articleRepository.insertArticle(articlePO);
System.out.println("update = " + update);
}
@Test
void testDelete() {
int update = articleRepository.deleteArticle(5);
System.out.println("update = " + update);
}
}
使用格式:@Result(column=" “,property=”“,one=@One(select=”"))
1.创建两个表的实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
//一对一关系
private ArticleDetail articleDetail;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ArticleDetail {
private Integer id;
private Integer articleId;
private String content;
}
2.创建Mapper查询接口
public interface ArticleOneToOneMapper {
//一对一查询
//查询文章详情
@Select("""
select id,article_id,content from article_detail
where article_id = #{articleId}
""")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "article_id", property = "articleId"),
@Result(column = "content", property = "content")
})
ArticleDetail selectDetail(Integer articleId);
//查询文章属性包含详情(内容)
@Select("""
select id,user_id,title,summary,read_count,create_time,update_time
from article where id = #{id}
""")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "user_id", property = "userId"),
@Result(column = "title", property = "title"),
@Result(column = "summary", property = "summary"),
@Result(column = "read_count", property = "readCount"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime"),
@Result(column = "id", property = "articleDetail",
one = @One(select = "com.hhb.mybatis.mapper.ArticleOneToOneMapper.selectDetail",
fetchType = FetchType.LAZY)
)
})
Article selectAllArticle(Integer id);
}
3.单元测试
@Resource
private ArticleOneToOneMapper articleOneToOneMapper;
@Test
void testOneToOne() {
Article article = articleOneToOneMapper.selectAllArticle(1);
System.out.println("article = " + article);
}
使用格式:@Result(property=“”, column=“”, many=@Many(select=“”))
1.创建COmmentPO实体
@Data
public class CommentPO {
private Integer id;
private Integer articleId;
private String content;
}
2.创建新的文章聚合实体
@Data
public class ArticleEntity {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
//多个评论
private List<CommentPO> comments;
}
3.新建Mapper接口
public interface ArticleCommentMapper {
//查询评论
@Select("""
select id,article_id,content from comment
where article_id=#{articleId} order by id;
""")
@Results(id = "CommentMapper", value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "article_id", property = "articleId"),
@Result(column = "content", property = "content")
})
List<CommentPO> selectComments(Integer articleId);
@Select("""
select id,user_id,title,summary,read_count,create_time,update_time
from article where id = #{articleId}
""")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "user_id", property = "userId"),
@Result(column = "title", property = "title"),
@Result(column = "summary", property = "summary"),
@Result(column = "read_count", property = "readCount"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime"),
@Result(column = "id", property = "comments",
many = @Many(select = "com.hhb.mybatis.mapper.ArticleCommentMapper.selectComments",
fetchType = FetchType.LAZY))
})
ArticleEntity selectArticleComment(Integer id);
}
4.单元测试
@Resource
private ArticleCommentMapper articleCommentMapper;
@Test
void testOneToMany() {
ArticleEntity articleEntity = articleCommentMapper.selectArticleComment(1);
System.out.println("articleEntity = " + articleEntity);
}
常用设置
#驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#mapper xml 文件位置
mybatis.mapper-locations=classpath:/mappers/**/*.xml
#启用缓存
mybatis.configuration.cache-enabled=true
#延迟加载
mybatis.configuration.lazy-loading-enabled=true
#mybatis 主配置文件,按需使用
mybatis.config-location=classpath:/mybatis-config.xml
mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<typeAliases>
<package name="com.hhb.po"/>
typeAliases>
configuration>
application.yml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Shanghai
username: root
password: 123456
hikari:
auto-commit: true
# # connections = ((cpu 核心数 * 2) + 磁盘数量) 近似值。 默认 10
maximum-pool-size: 10
#最小连接数,默认 10,不建议设置。默认与 maximum-pool-size 一样大小。推荐使用
固定大小的连接池
minimum-idle: 10
#获取连接时,检测语句
connection-test-query: select 1
###
# 连接超时,默认 30 秒。
# 控制客户端在获取池中 Connection 的等待时间,
# 如果没有连接可用的情况下超过该时间,则抛出 SQLException 异常,
###
connection-timeout: 20000
#其他属性
data-source-properties:
cachePrepStmts: true
dataSource.cachePrepStmtst: true
dataSource.prepStmtCacheSize: 250
dataSource.prepStmtCacheSqlLimit: 2048
dataSource.useServerPrepStmts: true
1.创建实体类
@Data//set、get方法
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
public class ArticlePO {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ArticleDetailPO {
private Integer id;
private Integer articleId;
private String content;
}
2.创建Mapper接口,创建两个方法,添加文章属性,文章内容
public interface ArticleMapper {
//添加新的文章
@Insert("""
insert into article(user_id,title,summary,read_count,create_time,update_time)
values(#{userId},#{title},#{summary},#{readCount},#{createTime},#{updateTime})
""")
//可选的配置,得到自动增长主键值
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
int insertArticle(ArticlePO article);
//添加的文章内容
@Insert("""
insert into article_detail (article_id, content)
values (#{articleId},#{content});
""")
int insertDetail(ArticleDetailPO detail);
}
3.创建Service接口,声明发布文章的方法
@Service
public class ArticleServiceImpl implements ArticleService {
@Resource
private ArticleMapper articleMapper;
@Override
public boolean postNewArticle(ArticlePO article, String content) {
//添加新的文章
int rows = articleMapper.insertArticle(article);
//添加文章内容
ArticleDetailPO detail = new ArticleDetailPO();
detail.setArticleId(article.getId());
detail.setContent(content);
int detailRows = articleMapper.insertDetail(detail);
return (rows + detailRows) == 2 ? true : false;
}
}
4.启动类
@MapperScan("com.hhb.trans.mapper")
@SpringBootApplication
public class Springboot11TransApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot11TransApplication.class, args);
}
}
5.编写配置文件
#配置数据源
#默认连接池,可以修改为其它的
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=030522
#设置自动提交
spring.datasource.hikari.auto-commit=true
#获取最大连接数,默认10
spring.datasource.hikari.maximum-pool-size=10
#获取连接时,检测语句
spring.datasource.hikari.connection-test-query=select 1
#配置mybatis
#支持驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#支持日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
6.单元测试
@SpringBootTest
class Springboot11TransApplicationTests {
@Resource
private ArticleService articleService;
@Test
void testAddArticle() {
ArticlePO article = new ArticlePO();
article.setTitle("Spring 事务管理");
article.setSummary("Spring 事务属性,事务实现");
article.setUserId(2001);
article.setReadCount(0);
article.setCreateTime(LocalDateTime.now());
article.setUpdateTime(LocalDateTime.now());
String content = "Spring 统一事务管理。事务管理器管理本地事务";
articleService.postNewArticle(article, content);
}
}
1.修改postNewArticle()方法添加@Transactional
@Service
//@Transactional
public class ArticleServiceImpl implements ArticleService {
@Resource
private ArticleMapper articleMapper;
/**
* @Transactional:事务控制注解 位置:1.方法的上面 2.类的上面
* 事务回滚:
* 1.默认对运行时异常,执行回滚rollback
* 2.rollbackFor:需要回滚的异常类列表
*/
@Transactional(rollbackFor = IOException.class)
@Override
public boolean postNewArticle(ArticlePO article, String content) {
//添加新的文章
int rows = articleMapper.insertArticle(article);
//抛出异常
if (article.getReadCount() < 1) {
throw new RuntimeException("文章的阅读数量最小是1");
}
//添加文章内容
ArticleDetailPO detail = new ArticleDetailPO();
detail.setArticleId(article.getId());
detail.setContent(content);
int detailRows = articleMapper.insertDetail(detail);
return (rows + detailRows) == 2 ? true : false;
}
}
2.启动类
//启用事务管理(可选)
@EnableTransactionManagement
@MapperScan("com.hhb.trans.mapper")
@SpringBootApplication
public class Springboot11TransApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot11TransApplication.class, args);
}
}
3.单元测试
@Test
void testAddArticleTrans() {
ArticlePO article = new ArticlePO();
article.setTitle("Spring666 事务管理");
article.setSummary("Spring666 事务属性,事务实现");
article.setUserId(new Random().nextInt(1000));
article.setReadCount(0);
article.setCreateTime(LocalDateTime.now());
article.setUpdateTime(LocalDateTime.now());
String content = "Spring666 统一事务管理。事务管理器管理本地事务";
articleService.postNewArticle(article, content);
}
1.接口中增加方法managerArticle
@Override
public boolean managerArticle(ArticlePO po, String content) {
//调用具有事务的方法
return postNewArticle(po, content);
}
2.单元测试,readCount为0
@Test
void testManagerArticleTrans() {
ArticlePO articlePO = new ArticlePO();
articlePO.setUpdateTime(LocalDateTime.now());
articlePO.setCreateTime(LocalDateTime.now());
articlePO.setTitle("===SpringMVC开发web应");
articlePO.setSummary("===基于MVC架构的");
articlePO.setUserId(new Random().nextInt(500));
articlePO.setReadCount(0);
String content="====Web开发使用SpringMVC";
boolean add = articleService.managerArticle(articlePO,content);
System.out.println("发布新的文章 = " + add);
}
1.修改接口方法的实现
@Override
@Transactional
public boolean postNewArticleThread(ArticlePO article, String content) {
System.out.println("Start 父线程:" + Thread.currentThread().threadId());
Thread thread = new Thread(()->{
System.out.println("子线程:"+Thread.currentThread().threadId());
//添加新的文章
int rows = articleMapper.insertArticle(article);
//抛出异常
if (article.getReadCount() < 1) {
throw new RuntimeException("文章的阅读数量最小是1");
}
//添加文章内容
ArticleDetailPO detail = new ArticleDetailPO();
detail.setArticleId(article.getId());
detail.setContent(content);
int detailRows = articleMapper.insertDetail(detail);
});
//线程启动
thread.start();
try {
//等他thread执行完成,在继续后面的代码
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("End 父线程:" + Thread.currentThread().threadId());
return true;
}
2.单元测试
@Test
void testManagerArticleTransThread() {
ArticlePO articlePO = new ArticlePO();
articlePO.setUpdateTime(LocalDateTime.now());
articlePO.setCreateTime(LocalDateTime.now());
articlePO.setTitle("Python开发web应");
articlePO.setSummary("基于Python MVC架构的");
articlePO.setUserId(new Random().nextInt(500));
articlePO.setReadCount(0);
String content = "Python开发使用Python";
boolean add = articleService.postNewArticleThread(articlePO, content);
System.out.println("发布新的文章 = " + add);
}
@Transactional注解的属性控制回滚