1、使用mybatis先配置,生成pom.xm配置,手动添加resource插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/>
</parent>
<groupId>com.zl</groupId>
<artifactId>study-springboot-mysql</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--web的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis的起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!--手动添加resources插件-->
<resources>
<resource>
<!--指定目录-->
<directory>src/main/java</directory>
<!--指定目录下的文件-->
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<!--plugins插件-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、添加application.yml的数据库源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yanhuo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: root
password:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis-plus:
mapper-locations: classpath*:/mapper/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.yanhuo.xo.model
global-config:
#数据库相关配置
db-config:
#主键类型
id-type: ASSIGN_ID
banner: false
#原生配置
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
jdbc-type-for-null: 'null'
configuration-properties:
prefix:
blobType: BLOB
boolValue: TRUE
多个Dao接口,可以在启动类使用@MapperScan(“basePackages= com.zl.dao ”),可是不能和@Mapper一起使用
如果需要使用xml写动态sql,我们需要在yml文件中,指定mapper.xml文件的位置
mybatis-plus:
mapper-locations: classpath*:/mapper/*.xml
需要在pom文件中,如开头手动添加resource的插件和配置
3、接下来就是对数据库的表进行CRUD
使用mybatis提供的接口BaseMapper对Mapper接口进行合适的操作
以及使用mybatis提供的接口IService对ServiceImpl实现类进行合适的操作
dao类继承BaseMapper的方法
@Mapper
public interface CommentDao extends BaseMapper<Comment> {
/**
* 得到所有回复消息
* @param page
* @param limit
* @param uid
* @return
*/
List<CommentVo> getAllReplyComment(long page, long limit, String uid);
}
xml实现dao类中的方法
<mapper namespace="com.yanhuo.platform.dao.CommentDao">
<select id="getAllReplyComment" resultType="com.yanhuo.xo.vo.CommentVo">
SELECT
c.id,
c.content,
c.create_date,
c.uid,
c.username,
c.avatar,
c.reply_id,
c.reply_name,
null as reply_uid,
null as reply_content,
i.id as mid,
i.cover as cover
FROM
t_comment c
JOIN t_img_detail i ON c.mid = i.id
WHERE
i.user_id = #{uid} and c.uid != #{uid}
AND c.pid = 0
union
SELECT DISTINCT
c.id,
c.content,
c.create_date,
c.uid,
c.username,
c.avatar,
c.reply_id,
c.reply_name,
s.uid as reply_uid,
s.content AS reply_content,
i.id as mid,
i.cover as cover
FROM
t_comment c
inner JOIN t_img_detail i ON c.mid = i.id
left join t_comment s ON c.reply_id = s.id
WHERE
i.user_id = #{uid} and c.uid != #{uid}
AND c.pid != 0
union
SELECT
s.id,
s.content,
s.create_date,
s.uid,
s.username,
s.avatar,
s.reply_id,
s.reply_name,
c.uid as reply_uid,
c.content AS reply_content,
i.id as mid,
i.cover as cover
FROM
t_comment c
left join t_comment s ON s.reply_id = c.id
left join t_img_detail i on s.mid = i.id
WHERE
c.uid = #{uid} and s.uid != #{uid}
order by
create_date desc
LIMIT ${(page-1)*limit},${limit};
</select>
</mapper>
public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int deleteById(T entity);
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
int delete(@Param("ew") Wrapper<T> queryWrapper);
int deleteBatchIds(@Param("coll") Collection<?> idList);
int updateById(@Param("et") T entity);
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
T selectById(Serializable id);
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);
} else {
return ts.get(0);
}
} else {
return null;
}
}
default boolean exists(Wrapper<T> queryWrapper) {
Long count = this.selectCount(queryWrapper);
return null != count && count > 0L;
}
Long selectCount(@Param("ew") Wrapper<T> queryWrapper);
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}
接口Service类继承IService的方法
@Service
public class AgreeCollectServiceImpl extends ServiceImpl<AgreeCollectDao, AgreeCollect> implements AgreeCollectService
{}
default boolean save(T entity) {
return SqlHelper.retBool(this.getBaseMapper().insert(entity));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveBatch(Collection<T> entityList) {
return this.saveBatch(entityList, 1000);
}
boolean saveBatch(Collection<T> entityList, int batchSize);
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return this.saveOrUpdateBatch(entityList, 1000);
}
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
default boolean removeById(Serializable id) {
return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
}
default boolean removeById(Serializable id, boolean useFill) {
throw new UnsupportedOperationException("不支持的方法!");
}
default boolean removeById(T entity) {
return SqlHelper.retBool(this.getBaseMapper().deleteById(entity));
}
default boolean removeByMap(Map<String, Object> columnMap) {
Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]);
return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap));
}
default boolean remove(Wrapper<T> queryWrapper) {
return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper));
}
default boolean removeByIds(Collection<?> list) {
return CollectionUtils.isEmpty(list) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean removeByIds(Collection<?> list, boolean useFill) {
if (CollectionUtils.isEmpty(list)) {
return false;
} else {
return useFill ? this.removeBatchByIds(list, true) : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
}
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean removeBatchByIds(Collection<?> list) {
return this.removeBatchByIds(list, 1000);
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean removeBatchByIds(Collection<?> list, boolean useFill) {
return this.removeBatchByIds(list, 1000, useFill);
}
default boolean removeBatchByIds(Collection<?> list, int batchSize) {
throw new UnsupportedOperationException("不支持的方法!");
}
default boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
throw new UnsupportedOperationException("不支持的方法!");
}
default boolean updateById(T entity) {
return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
}
default boolean update(Wrapper<T> updateWrapper) {
return this.update((Object)null, updateWrapper);
}
default boolean update(T entity, Wrapper<T> updateWrapper) {
return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean updateBatchById(Collection<T> entityList) {
return this.updateBatchById(entityList, 1000);
}
boolean updateBatchById(Collection<T> entityList, int batchSize);
boolean saveOrUpdate(T entity);
default T getById(Serializable id) {
return this.getBaseMapper().selectById(id);
}
default List<T> listByIds(Collection<? extends Serializable> idList) {
return this.getBaseMapper().selectBatchIds(idList);
}
default List<T> listByMap(Map<String, Object> columnMap) {
return this.getBaseMapper().selectByMap(columnMap);
}
default T getOne(Wrapper<T> queryWrapper) {
return this.getOne(queryWrapper, true);
}
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
Map<String, Object> getMap(Wrapper<T> queryWrapper);
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
default long count() {
return this.count(Wrappers.emptyWrapper());
}
default long count(Wrapper<T> queryWrapper) {
return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper));
}
default List<T> list(Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectList(queryWrapper);
}
default List<T> list() {
return this.list(Wrappers.emptyWrapper());
}
default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectPage(page, queryWrapper);
}
default <E extends IPage<T>> E page(E page) {
return this.page(page, Wrappers.emptyWrapper());
}
default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectMaps(queryWrapper);
}
default List<Map<String, Object>> listMaps() {
return this.listMaps(Wrappers.emptyWrapper());
}
default List<Object> listObjs() {
return this.listObjs(Function.identity());
}
例子:
简单的就用new QueryWrapper() 直接查询了,不用写入xml,复杂的才要xml
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAlbum(String id, String uid) {
//得到当前专辑下的所有图片
List<AlbumImgRelation> albumImgRelationEntityList = albumImgRelationService.list(new QueryWrapper<AlbumImgRelation>().eq("aid", id));
List<String> idList = albumImgRelationEntityList.stream().map(e -> String.valueOf(e.getMid())).collect(Collectors.toList());
String albumStateKey = PlatformConstant.ALBUM_STATE + id;
redisUtils.delete(albumStateKey);
imgDetailService.deleteImgs(idList, uid);
this.removeById(id);
}
4、实例类
dao类和service类和Impl
Dao接口是mapper映射操作
@Service
public class ChatServiceImpl implements ChatService {
@Autowired
MessageDao messageDao;
@Transactional(rollbackFor = Exception.class)
@Override
public void addChatRecord(MessageDTO messageDTO) {
//往数据库里面添加记录,
Message messageEntity = ConvertUtils.sourceToTarget(messageDTO, Message.class);
messageEntity.setTime(String.valueOf(System.currentTimeMillis()));
messageDao.insert(messageEntity);
`````
}
数据库表名的类是数据库中表数据的映像到JAVA中的对象类
@Data
@TableName("t_message")
public class Message extends BaseEntity{
}
@Data
public abstract class BaseEntity implements Serializable {
/**
* id
*/
@TableId
private Long id;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long creator;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createDate;
}
其中
``@TableField(fill = FieldFill.INSERT)` 是 MyBatis-Plus 框架中的注解,用于指定数据库字段在插入操作时的填充方式。这个注解通常与实体类中的字段一起使用。
@TableField
注解用于标识数据库表字段与实体类属性的映射关系。fill
参数指定了填充策略,FieldFill.INSERT
表示在执行插入操作时填充该字段。
这个注解的作用是在执行插入操作时自动填充数据库表的相应字段,通常用于设置数据库表的创建时间或者创建者等信息,以简化开发者的操作。
@TableField
是 MyBatis-Plus 中的一个注解,用于标识实体类字段与数据库表字段的映射关系,并提供了一些属性来配置该映射关系的细节。
常用的属性包括:
-
value
:指定该字段映射的数据库表字段名。如果实体类字段名与数据库表字段名相同,则可以省略该属性。@TableField("user_name") private String userName;
-
exist
:指定该字段是否为数据库表中的实际字段。当设置为 false 时,表示该字段只在业务逻辑中使用,而不对应数据库表中的实际字段,默认为 true。@TableField(value = "is_deleted", exist = false) private Boolean deleted;
-
el
:指定一个 EL 表达式,用于动态计算该字段的值。@TableField(el = "userType == 'ADMIN' ? 'Y' : 'N'") private String isAdmin;
-
fill
:指定在插入或更新操作时,自动填充该字段的策略。@TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.UPDATE) private LocalDateTime updateTime;
-
condition
:指定更新操作时,仅当满足指定条件时才更新该字段的值。@TableField(value = "last_modified", condition = "%s != null") private LocalDateTime lastModified;
这些属性可以根据实际业务需求进行组合使用,以实现灵活的映射配置和字段行为控制。
如果实体类中的字段不是对应本表的字段,而是对应其他表的字段,可以通过 @TableField
注解的 value
属性来指定该字段在本表中的映射字段名。这样,在查询或操作本表时,就可以使用该字段,而实际操作的是其他表的数据。
举个例子,假设有两个表 user
和 role
,在 user
表中有一个字段 role_id
,用来表示用户的角色,在 role
表中有一个字段 role_name
,表示角色的名称。如果在 User
实体类中需要使用角色名称,可以这样配置:
javaCopy Codepublic class User {
private Long id;
@TableField(value = "role_name", exist = false)
private String roleName; // 该字段不是本表字段,而是与 role 表关联的字段
// 其他字段和方法省略
}
在上述配置中,roleName
字段并不是 user
表中的实际字段,而是表示用户角色的名称,通过 @TableField
注解的 value
属性指定了在 user
表中的映射字段名为 role_name
。这样,当查询 User
对象时,可以通过 roleName
字段获取用户对应的角色名称,虽然实际操作的是 role
表中的数据。
@TableId
是 MyBatis-Plus 中的一个注解,用于标识实体类中的主键字段。它的作用是告诉 MyBatis-Plus 这个字段是数据库表的主键,以便在执行 CRUD 操作时进行识别和处理。
常用的属性包括:
-
value
:指定主键字段的名称,如果实体类中的字段名与数据库表中的主键字段名相同,则可以省略该属性。javaCopy Code@TableId("user_id") private Long id;
-
type
:指定主键生成策略,常见的有 AUTO、INPUT、ID_WORKER、ID_WORKER_STR 等,根据实际情况选择合适的主键生成策略。AUTO
:数据库自增,适用于支持自增主键的数据库,如 MySQL、SQL Server 等。INPUT
:手动输入,需要在插入数据时手动设置主键值。ID_WORKER
:数字类型的全局唯一 ID,使用雪花算法生成,适用于分布式环境。ID_WORKER_STR
:字符串类型的全局唯一 ID,同样使用雪花算法生成。
javaCopy Code@TableId(value = "id", type = IdType.AUTO) private Long id;
-
exist
:指定该字段是否为数据库表中的实际字段。当设置为 false 时,表示该字段只在业务逻辑中使用,而不对应数据库表中的实际字段,默认为 true。javaCopy Code@TableId(value = "id", exist = false) private Long id;
通过 @TableId
注解标识主键字段,可以使 MyBatis-Plus 自动生成符合预期的主键值,并在执行插入、更新、删除等操作时正确识别主键字段,从而简化开发工作。`
DTO类是数据库数据转换为JAVA对象后,用于校验和操作的对象类
@Data
public class AgreeCollectDTO implements Serializable {
@ApiModelProperty(value = "当前点赞的用户id")
@NotNull(message = "uid不能为空", groups = DefaultGroup.class)
private Long uid;
@ApiModelProperty(value = "点赞的类型id")
@NotNull(message = "点赞id不能为空", groups = DefaultGroup.class)
private Long agreeCollectId;
@ApiModelProperty(value = "点赞图片或评论发布的用户id")
@NotNull(message = "给他人点赞id不能为空", groups = DefaultGroup.class)
private Long agreeCollectUid;
@ApiModelProperty(value = "0代表点赞评论,1代表点赞图片,2代表收藏图片,3是收藏专辑")
@InValues(vals = {0, 1, 2, 3}, groups = DefaultGroup.class)
private Integer type;
}
对比原来数据库中的源类
@Data
@ApiModel(description = "点赞收藏")
@TableName("t_agree_collect")
public class AgreeCollect extends BaseEntity {
private Long uid;
private Long agreeCollectId;
private Long agreeCollectUid;
private Integer type;
}
VO类是返回给前端的JSON类,所以内容经过整合了,内容可能包括表名类和相关的表名关系类及其他相关的类,可以对比上面的DTO类和未修改的源类
public CommentVo addComment(CommentDTO commentDTO){}
@Data
@Accessors(chain = true)
public class AgreeCollectVo implements Serializable {
private Long aid;
private Long mid;
private String cover;
private Long uid;
private String username;
private String avatar;
private String content;
private String name;
private Integer count;
/**
* 图片数量
*/
private Long imgCount;
/**
* 收藏数量
*/
private Long collectionCount;
// 0是评论,1是图片,2专辑
private Integer type;
private Date createDate;
}
@Mapper
public interface AgreeCollectDao extends BaseMapper<AgreeCollect> {
}
public interface AgreeCollectService extends IService<AgreeCollect> {
}
@Service
public class AgreeCollectServiceImpl extends ServiceImpl<AgreeCollectDao, AgreeCollect> implements AgreeCollectService {
}
//因为继承了DAO类,使用可以通过this引用 直接使用DAO类中BaseMapper接口的方法
可以使用save、insert、
this.getOne(new QueryWrapper<AgreeCollect>().eq("name","name").like("name","%丽%"));
等等
Impl 是实现类
public interface AgreeCollectService extends IService<AgreeCollect>{}
@Service
public class AgreeCollectServiceImpl extends ServiceImpl<AgreeCollectDao, AgreeCollect> implements AgreeCollectService
在Controller中可以之间使用Serviced 接口,因为子类Impl的Bean会注入AgreeCollectService的@Autowired的变量中
@Autowired
private AgreeCollectService agreeCollectService;
…
…
5、实例类之间转换方法
BeanUtils.copyProperties 是 Apache Commons BeanUtils 库中的一个方法,用于将一个 JavaBean 的属性值复制到另一个 JavaBean 中。这个方法通常用于简化对象之间的属性复制工作,比如将一个 DTO(Data Transfer Object)对象的属性复制到一个持久化实体对象中。
具体用法如下:
javaCopy Code// 导入需要的包
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
// 复制属性
BeanUtils.copyProperties(destBean, origBean);
在这个方法中,destBean 是目标对象,origBean 是源对象,该方法会自动匹配两个对象中对应属性名的值,并进行复制。需要注意的是,BeanUtils.copyProperties 方法只复制属性名和类型相同的属性,不会复制类型不同的属性,也不会进行深度复制。
需要留意的是,在使用 BeanUtils.copyProperties 方法时,如果两个对象中存在同名但不同类型的属性,会抛出 NoSuchMethodException 异常,因此在使用之前需要确保源对象和目标对象的属性类型一致。
public static <T> T sourceToTarget(Object source, Class<T> target){
if(source == null){
return null;
}
T targetObject = null;
try {
targetObject = target.newInstance();
BeanUtils.copyProperties(source, targetObject);
} catch (Exception e) {
logger.error("convert error ", e);
}
return targetObject;
}
public void saveAlbum(AlbumDTO albumDTO) {
Album albumEntity = ConvertUtils.sourceToTarget(albumDTO, Album.class);
this.save(albumEntity);
}
@Data//隐式完成setter和getter、toString
@ApiModel(value = "专辑")
public class AlbumDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "专辑id")
private Long id;
@ApiModelProperty(value = "专辑名称")
@NotBlank(message = "内容不能为空", groups = DefaultGroup.class)
private String name;
@ApiModelProperty(value = "专辑发布的用户id")
@NotNull(message = "用户id不能为空", groups = DefaultGroup.class)
private Long uid;
@ApiModelProperty(value = "专辑封面")
private String cover;
@ApiModelProperty(value = "专辑排序")
private Integer sort;
}
@Data
@TableName("t_album")
public class Album extends BaseEntity {
/**
*专辑名称
*/
private String name;
/**
* 专辑发布的用户id
*/
private Long uid;
/**
* 专辑封面
*/
private String cover;
/**
* 专辑排序
*/
private Integer sort;
/**
* 图片数量
*/
private Long imgCount;
/**
* 收藏数量
*/
private Long collectionCount;
/**
* 修改用户
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}
6、事务支持
事务支持(Transaction Support)是指在数据库管理系统中,对于一组相关的数据库操作,确保这组操作要么全部成功执行,要么全部失败回滚的机制。它是保证数据库的一致性和可靠性的重要手段。
在数据库中,事务是由一系列的数据库操作组成的逻辑单位。事务支持提供了以下几个关键特性:
- 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部失败回滚。如果一个操作失败,那么之前已经执行的操作都会被撤销,数据库返回到事务开始前的状态,保证数据库的一致性。
- 一致性(Consistency):事务的执行不会破坏数据库的完整性约束条件,即使在并发执行的情况下也能保证数据的一致性。
- 隔离性(Isolation):并发执行的事务之间应该相互隔离,每个事务的执行应当与其他事务的执行相互独立,互不干扰。事务的隔离性能够避免并发执行时可能出现的问题,如脏读、不可重复读、幻读等。
- 持久性(Durability):一旦事务提交成功,其所做的修改将永久保存在数据库中,即使发生系统故障或重启,也不会丢失。
事务支持的主要目的是确保数据库的数据一致性和可靠性。通过将一组相关的操作封装在事务中,可以保证这些操作要么全部成功执行,要么全部回滚,从而避免了因为部分操作失败而导致数据库处于不一致的状态。同时,事务支持还能够提高数据库的并发性能,允许多个事务同时执行而互不干扰。
Spring框架中的事务:
(1)使用管理事务的对象: 事务管理器(接口, 接口有很多的实现类)
例:使用Jdbc或mybatis访问数据库,使用的事务管理器:DataSourceTransactionManager
(2)声明式事务: 在xml配置文件或者使用注解说明事务控制的内容
控制事务: 隔离级别,传播行为, 超时时间等
(3)事务处理方式:
①Spring框架中的@Transactional;
②aspectj框架可以在xml配置文件中,声明事务控制的内容;
SpringBoot使用事务非常简单,底层依然采用的是 Spring 本身提供的事务管理
①在业务方法的上面加入@Transactional , 加入注解后,方法有事务功能了。
②在主启动类的上面 ,加入@EnableTransactionManager,开启事务支持。
如果只是springboot添加事务功能,只可以在服务器回滚事务,在mysql数据库中却可以回滚,所以需要在注解中添加rollbackFor=Exception.class,使得数据库也会回滚
@Override
@Transactional(rollbackFor = Exception.class)
public CommentVo addComment(CommentDTO commentDTO){}
注:只加上@Transactional也能完成事务的功能,对于@EnableTransactionManager建议也加上。