持续热爱!
Consistency,一致性。Availability,可用性。Partition tolerance,分区容错性。
CAP指出在分布式系统中,不可能同时满足以上三点。
首先一个分布式系统,必然是要考虑P的,即通过将数据复制到多个节点来提高容错性。
而如果要选择A、C,放弃P,不将数据备份到多个节点。那么当然,不用考虑不同结点间数据的同步,一致性就不存在了,而不用考虑一致性问题,能够保证响应时间及时响应用户,可用性问题也就解决了。
每个属性都不可再分。即每个列不可再分。 1NF是所有关系型数据库的最基本要求。
简单来说,就是通过x
可以唯一的确定y
,比如身份证号可以唯一的确定人名。 那么,就称x函数确定y或y函数依赖于x。记作 x→y
。
x
称为这个函数依赖的决定属性组,也称为决定因素。
若 x→y,y→x,则记作 x←→y。 若y函数不依赖于x,则记作
若 x→y ,但y
不是x
的子集,则称 x→y 是非平凡的函数依赖。 若 x→y ,但y
是x
的子集,则称 x→y 是平凡的函数依赖。
例子,以学生学号sno
,课程编号cno
,学生课程成绩grade
为例。
若 x → y,且 x 的任何一个真子集 x',都不可能 x' → y,则称 y 对 x 完全函数依赖。 记作
比如通过学生学号sno
,课程编号cno
可以确定学生该课程的成绩grade
。 但无法从(sno, cno)的任何真子集中如sno
确定学生某一课程的成绩。则称grade
对(sno, cno)完全函数依赖。
若 x → y,但 y 不完全函数依赖于 x,则称 y 对 x 部分函数依赖。 记作
比如身份证号码id
和手机号码phone
可以确定姓名name
。 但存在存在 id → name
,即部分函数依赖。
sno
→ 系名dept_name
, 系名dept_name
→ 系主任dept_manager_name
。 且dept_name
不是 → sno
,不是sno
的子集。所以dept_manager_name
传递函数依赖于sno
。若
则称k为候选码(Candidate Key)。若候选码多于一个,则选定其中一个作为主码(Primary Key,主键)。
如身份证号码id
、学号sno
都能确定学生的姓名sname
,如下表。 那么id
、sno
为码。若选择id
为主键,即主码。
id | sno | sname |
---|---|---|
cno(课程号) | sno(学号) | grade(学生课程成绩) |
---|---|---|
sdept
。cno(课程号) | sno(学号) | grade(学生课程成绩) | sdept(学生系名) |
---|---|---|---|
2NF就是消除下图中的虚线。
cno(课程号) | sno(学号) | grade(学生课程成绩) |
---|---|---|
sno(学号) | sdept(学生系名) |
---|---|
sno
是码,sno → sdept,sdept → sloc,且符合传递依赖的定义,所以 sloc 传递依赖于 sno。sno(学号) | sdept(学生系名) | sloc(学生宿舍地址,同一个系的学生一个地址) |
---|---|---|
sno(学号) | sdept(学生系名) |
---|---|
sdept(学生系名) | sloc(学生宿舍地址,同一个系的学生一个地址) |
---|---|
TransactionDefinition
接口中规定了7种类型的事务传播行为。 事务传播行为是Spring框架独有的事务增强特性,不属于事务实际提供方即数据库的行为。事务传播行为类型 | 说明 |
---|---|
REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则创建一个事务。 |
x代码解读复制代码<?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.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>fun.roran</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<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>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- lombok start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- lombok end -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
xxxxxxxxxx
代码解读复制代码server:
port: 80
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
username: root
password: 123456
xxxxxxxxxx
代码解读复制代码CREATE TABLE `tx` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
xxxxxxxxxx
代码解读复制代码package fun.roran.demo.dao;
public interface TxDAO {
void a();
void b();
}
xxxxxxxxxx
代码解读复制代码package fun.roran.demo.dao.impl;
import fun.roran.demo.dao.TxDAO;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository
public class TxDAOImpl implements TxDAO {
@Resource
JdbcTemplate jdbcTemplate;
@Override
public void a() {
jdbcTemplate.execute("INSERT INTO tx (value) VALUES ('a')");
}
@Override
public void b() {
jdbcTemplate.execute("INSERT INTO tx (value) VALUES ('b')");
}
}
xxxxxxxxxx
代码解读复制代码package fun.roran.demo.service;
public interface TxServiceA {
void a();
}
xxxxxxxxxx
代码解读复制代码package fun.roran.demo.service.impl;
import fun.roran.demo.dao.TxDAO;
import fun.roran.demo.service.TxServiceA;
import fun.roran.demo.service.TxServiceB;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class TxServiceAImpl implements TxServiceA {
@Resource
private TxDAO txDAO;
@Resource
private TxServiceB txServiceB;
@Override
public void a() {
txDAO.a();
txServiceB.b();
}
}
xxxxxxxxxx
代码解读复制代码package fun.roran.demo.service;
public interface TxServiceB {
void b();
}
xxxxxxxxxx
代码解读复制代码package fun.roran.demo.service.impl;
import fun.roran.demo.dao.TxDAO;
import fun.roran.demo.service.TxServiceB;
import javax.annotation.Resource;
public class TxServiceBImpl implements TxServiceB {
@Resource
private TxDAO txDAO;
@Override
public void b() {
txDAO.b();
}
}
a()
开启事务a()
开启事务的情况下,a()
、b()
相当于处于同一个事务。它们要么全都提交,要么全都不提交。b()
抛出异常,a()
、b()
会一起回滚。 注意,即使b()
的异常被a()
捕获,仍是要一起回滚的。a()
调用完b()
后抛出异常回滚,b()
也会回滚。
a()
不开启事务a()
不开启事务,b()
开启事务。a()
调用完b()
后抛出异常,b()
自然不会回滚。 同样若b()
抛出异常回滚,也不会影响a()
已经执行过的代码。
a()
开启事务a()
开启事务的情况下,b()
会加入a()
的事务。二者属于同一事务。b()
抛出异常,a()
、b()
会一起回滚。 注意,即使b()
的异常被a()
捕获,仍是要一起回滚的。a()
调用b()
后抛出异常,则b()
也会回滚。
a()
不开启事务a()
不开启事务,则a()
、b()
都处在非事务环境下。
a()
开启事务a()
开启事务的情况下,b()
会加入a()
的事务。二者属于同一事务。b()
抛出异常,a()
、b()
会一起回滚。 注意,即使b()
的异常被a()
捕获,仍是要一起回滚的。a()
调用b()
后抛出异常,则b()
也会回滚。
a()
不开启事务b()
时会抛出异常。xxxxxxxxxx
代码解读
复制代码org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
a()
开启事务a()
开启事务,执行b()
时,会将a()
的事务挂起,b()
创建一个自己的事务。b()
抛出异常回滚,若a()
没有对异常进行补获,则也要回滚。 若a()
捕获了异常,则a()
不用回滚。a()
调用完b()
后抛出异常,a()
回滚,b()
不用回滚(因为b()
的事务已经提交了)。
a()
不开启事务b()
会开启一个自身的事务。 b()
若发生异常回滚不会影响到a()
已执行操作,a()
调用完b()
后抛出异常自然不会影响到b()
。
a()
开启事务a()
开启事务,但在执行b()
的过程中,a()
的事务被挂起且b()
是无事务方式执行的。a()
调用b()
后抛出异常,则b()
不会被回滚(除了其他b()
的其它操作可以回滚)。b()
执行中抛出异常,则b()
已经执行的操作也不会回滚。 同时若a()
没有捕获b()
抛出的异常,a()
也会被回滚。若a()
捕获了,则不会被回滚。a()
、b()
哪个操作抛出异常且未捕获,a()
一定会被回滚,b()
一定不会回滚。
a()
不开启事务a()
、b()
都处于无事务状态。a()
开启事务b()
抛出异常。xxxxxxxxxx
代码解读
复制代码org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
a()
不开启事务a()
、b()
都处于无事务状态。a()
开启事务b()
时,开启一个事务,这个事务是a()
的子事务。b()
抛出异常并回滚时。该异常若被a()
捕获,则a()
不会回滚,否则a()
回滚。a()
调用b()
后抛出异常,则a()
、b()
一起回滚。a()
不开启事务b()
开启了事务。
a()
、b()
方法写在同一个类中,那么事务传播行为将失效。xxxxxxxxxx
代码解读复制代码package fun.roran.demo.service.impl;
import fun.roran.demo.dao.TxDAO;
import fun.roran.demo.service.TxServiceA;
import fun.roran.demo.service.TxServiceB;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class TxServiceAImpl implements TxServiceA {
@Resource
private TxDAO txDAO;
@Transactional(rollbackFor = Exception.class)
@Override
public void a() {
txDAO.a();
b();
}
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
@Override
public void b() {
txDAO.b();
}
}
a()
方法,发现能够正常运行,不抛出异常。TxServiceAImpl
对象生产代理对象后,代理对象底层还是调用TxServiceAImpl
对象的b()
方法,而这个方法是不支持事务传播功能的。this
调用方法,而是通过注入代理对象类调用。xxxxxxxxxx
代码解读复制代码package fun.roran.demo.service.impl;
import fun.roran.demo.dao.TxDAO;
import fun.roran.demo.service.TxServiceA;
import fun.roran.demo.service.TxServiceB;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class TxServiceAImpl implements TxServiceA {
@Resource
private TxDAO txDAO;
@Resource
private TxServiceA txServiceA;
@Transactional(rollbackFor = Exception.class)
@Override
public void a() {
txDAO.a();
txServiceA.b();
}
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
@Override
public void b() {
txDAO.b();
}
}
public
方法@Transactional
只能用于 public 的方法上,否则事务失效。 如果要用在非public
方法上,可以开启 AspectJ 代理模式。