Answers:
好的问题,尽管不是一个简单的答案。
定义事务之间的关系。常用选项:
Required
:代码将始终在事务中运行。创建一个新事务或重用一个事务(如果有)。Requires_new
:代码将始终在新事务中运行。如果存在当前事务,则将其挂起。定义事务之间的数据契约。
Read Uncommitted
:允许脏读。Read Committed
:不允许脏读。Repeatable Read
:如果在同一事务中两次读取一行,结果将始终相同。Serializable
:按顺序执行所有事务。在多线程应用程序中,不同的级别具有不同的性能特征。我认为,如果您了解dirty reads
概念,便可以选择一个不错的选择。
何时发生脏读的示例:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
因此,可以设置一个合理的默认值(如果可以要求的话)Read Committed
,它只能让您读取传播级别为的其他正在运行的事务已提交的值Required
。然后,如果您的应用程序有其他需求,则可以从那里开始工作。
一个实际的示例,该示例在进入provideService
例程时始终在其中创建新事务,而在离开时总是在其中完成:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
如果我们改为使用Required
,则在进入例程时如果事务已经打开,则事务将保持打开状态。还要注意,a的结果rollback
可能会有所不同,因为多次执行可能会参与同一事务。
我们可以通过测试轻松验证行为,并查看结果随传播级别的不同:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
传播水平为
Requires new
:我们希望fooService.provideService()
它不会回滚,因为它创建了它自己的子事务。
Required
:我们希望一切都回滚而后备存储保持不变。
sessionFactory.getCurrentTransaction()
添加以来,不再需要运行HibernateTemplate
来管理事务。我将其删除了:)
PROPAGATION_REQUIRED = 0 ; 如果方法M1的DataSourceTransactionObject T1已经启动,如果另一个方法M2需要事务对象,则不会创建新的事务对象.M2使用相同的对象T1
PROPAGATION_MANDATORY = 2 ; 方法必须在事务中运行。如果没有现有的交易正在进行,则将引发异常
PROPAGATION_REQUIRES_NEW = 3 ; 如果方法M1的DataSourceTransactionObject T1已经启动并且正在执行中(正在执行方法M1)。如果另一个方法M2开始执行,则T1在方法M2的持续时间内暂停,同时M2的新DataSourceTransactionObject T2在其自身的事务上下文中运行
PROPAGATION_NOT_SUPPORTED = 4 ; 如果方法M1的DataSourceTransactionObject T1已经启动,如果另一个方法M2同时运行,则M2不应在事务上下文中运行。T1暂停直到M2完成。
PROPAGATION_NEVER = 5 ; 这些方法均未在事务上下文中运行。
隔离级别: 它是关于一个事务可能受到其他并发事务的活动影响的程度。它支持一致性,使许多表中的数据保持一致状态。它涉及锁定数据库中的行和/或表。
多笔交易的问题
方案1.如果T1事务从另一个并发事务T2写入的表A1中读取数据,如果在T2进行回滚的途中,则T1获得的数据无效。例如a = 2是原始数据。如果T1读取a = T2回写的1
方案2如果T1事务从表A1读取数据,如果另一个并发事务(T2)更新表A1上的数据,则T1读取的数据与表A1不同,因为T2已更新表A1上的数据,例如T1读取a = 1并且T2更新了a = 2。然后a!= b。
方案3。如果T1事务从表A1中读取具有一定行数的数据。如果另一个并发事务(T2)在表A1上插入更多行,则T1读取的行数与表A1上的行数不同
方案1称为脏读。
方案2称为不可重复读取。
方案3称为幻影读取。
因此,隔离级别是可以防止方案1,方案2,方案3扩展的范围。您可以通过实现锁定来获得完整的隔离级别,这可以防止发生对同一数据的并发读写,但是这会影响性能。隔离级别取决于应用程序对应用程序需要多少隔离。
ISOLATION_READ_UNCOMMITTED:允许读取尚未提交的更改。它受方案1,方案2,方案3的影响
ISOLATION_READ_COMMITTED:允许从已提交的并发事务中读取。它可能会遇到方案2和方案3。因为其他事务可能正在更新数据。
ISOLATION_REPEATABLE_READ:对同一字段的多次读取将产生相同的结果,直到被自己更改为止。这可能会受到方案3的影响,因为其他事务可能正在插入数据
ISOLATION_SERIALIZABLE:场景1,场景2,场景3永远不会发生,它是完全隔离的,涉及完全锁定,由于锁定而影响性能。
您可以使用测试
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
您可以调试并查看具有不同值的结果,以进行隔离和传播。
关于其他参数的足够解释由其他答案给出;但是,您要求提供一个真实的示例,下面的示例阐明了不同传播选项的目的:
假设您负责实施注册服务,通过该服务将确认电子邮件发送给用户。您想到了两个服务对象,一个用于注册用户,另一个用于发送电子邮件,后者在第一个中被称为。例如这样的事情:/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
您可能已经注意到第二个服务的传播类型为REQUIRES_NEW,而且有可能引发异常(SMTP服务器关闭,电子邮件无效或其他原因)。您可能不希望整个过程回滚,例如从数据库或其他事物中删除用户信息;因此,您在单独的事务中调用第二个服务。
回到我们的示例,这次您担心数据库的安全性,因此您可以通过以下方式定义DAO类:/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
这就意味着无论何时创建DAO对象,从而可能创建对db的访问,我们都需要确保从我们的服务之一内部进行调用,这意味着应该存在一个实时事务。否则将发生异常。因此,传播的类型为MANDATORY。
隔离级别定义一个事务对某个数据存储库所做的更改如何影响其他同时进行的并发事务,以及更改后的数据如何以及何时可用于其他事务。当我们使用Spring框架定义事务时,我们还可以配置将在同一隔离级别执行同一事务。
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
READ_UNCOMMITTED隔离级别指出一个事务可能读取其他事务仍未提交的数据。
READ_COMMITTED隔离级别指出,一个事务无法读取其他事务尚未提交的数据。
REPEATABLE_READ隔离级别指出,如果一个事务多次从数据库读取一条记录,则所有这些读取操作的结果必须始终相同。
SERIALIZABLE隔离级别是所有隔离级别中最严格的。事务在所有级别都被锁定执行(读,范围和写锁定),因此它们看起来好像是以串行化方式执行的。
传播是决定如何在逻辑或物理事务中封装业务方法的能力。
Spring REQUIRED行为意味着,如果当前bean方法执行上下文中已有打开的事务,则将使用同一事务。
REQUIRES_NEW行为意味着容器将始终创建新的物理事务。
NESTED行为使嵌套的Spring事务使用相同的物理事务,但在嵌套调用之间设置保存点,因此内部事务也可以独立于外部事务进行回滚。
MANDATORY行为指出现有的已打开事务必须已经存在。否则,容器将引发异常。
NEVER行为指出现有的已打开事务必须不存在。如果存在事务,则容器将引发异常。
NOT_SUPPORTED行为将在任何事务范围之外执行。如果已经存在一个已打开的事务,它将被暂停。
如果已存在打开的事务,则SUPPORTS行为将在事务范围内执行。如果还没有打开的事务,则该方法无论如何都将执行,但是将以非事务方式执行。
事务表示数据库的工作单元。
在Spring TransactionDefinition
界面中,该界面定义了Spring兼容的事务属性。@Transactional
批注描述方法或类上的事务属性。
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
传播(复制):用于内部交易关系。(类似于java线程间通信)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
隔离:隔离是数据库事务的ACID(原子性,一致性,隔离性,耐久性)属性之一。隔离度确定其他用户和系统如何查看事务完整性。它用于资源锁定,即并发控制,请确保在给定点只有一个事务可以访问资源。
锁定感知:隔离级别决定了锁定的持续时间。
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
阅读感知:发生以下3种主要问题:
UPDATES
从另一个TX 提交的读取。INSERTS
和/或DELETES
从另一个TX 读取具有不同读取类型的隔离级别:
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
您几乎永远不想使用Read Uncommited
它,因为它实际上并不ACID
合规。Read Commmited
是一个很好的默认起点。Repeatable Read
仅在报告,汇总或聚合方案中才需要。请注意,包括Postgres在内的许多数据库实际上并不支持重复读取,Serializable
而必须使用它。Serializable
对于您必须完全独立于其他任何事情发生的事情很有用;就像synchronized
在Java中那样想。可序列化与REQUIRES_NEW
传播齐头并进。
我将REQUIRES
所有用于运行UPDATE或DELETE查询的功能以及“服务”级功能用于所有功能。对于仅运行SELECT的DAO级别的函数,SUPPORTS
如果已经启动(即从服务函数调用),我将使用该函数参与TX。
事务隔离和事务传播虽然相关,但是显然是两个非常不同的概念。在这两种情况下,都可以使用声明式事务管理或程序化事务管理在客户端边界组件上自定义默认值。可以在下面的参考链接中找到每个隔离级别和传播属性的详细信息。
对于给定的两个或多个与数据库的运行中事务/连接,一个事务中的查询如何和何时进行更改会影响/可见另一事务中的查询。它还与将使用哪种数据库记录锁定来隔离此事务中的更改与其他事务(反之亦然)有关。这通常由参与事务的数据库/资源来实现。
。
在针对任何给定请求/处理的企业应用程序中,涉及许多组件以完成工作。其中一些组件标记了将在各个组件及其子组件中使用的事务的边界(开始/结束)。对于组件的该事务边界,Transaction Propogation指定各个组件是否将参与事务,以及如果调用的组件已经创建或未启动事务,会发生什么情况。这与Java EE事务属性相同。这通常由客户端事务/连接管理器实现。
参考:
我已经运行了outerMethod
,method_1
并且method_2
具有不同的传播模式。
以下是不同传播模式的输出。
外部方法
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
方法_1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
方法_2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
我们可以为此添加:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
您可以这样使用:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
您还可以使用此东西:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}