当前位置:网站首页>New programmers optimize a line of code on Monday and are discouraged on Wednesday?

New programmers optimize a line of code on Monday and are discouraged on Wednesday?

2022-06-21 09:43:00 Programmer Xiaohui

445accd2402c32fc94bf1a418b1ca90d.png

This article is written by silent Wang Er (id:cmower) Authorized reprint

If you reprint, please contact the official account.

This Monday , A new colleague of the company , I did very well in the interview , All kinds of questions were answered fluently , The boss and I are very happy .

Such an excellent person , Never let him waste a minute , So soon , I sent him the requirements document 、 Source code , Let him familiarize himself with the business and development process locally .

It didn't work out , On Wednesday, let's  review I found the problem when I was writing the code , The new colleague directly changed the original  @Transactional  Optimization is like this :

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

Just because of this line of code , Boss ( At that time, he was also a good player in the first-line Internet factory ) Got mad on the spot , This new colleague will soon be persuaded to leave , I'll make it right away , After all, the person you interview , Don't look at the Buddha's face , Is that so? ? So the boss promised me to try it for another month .

After the meeting , I hurriedly asked my new colleague to review the affairs , Here is his own summary , Still very detailed , Share it to give you a little reference and inspiration . I believe you will understand why you can't optimize it like this  @Transactional  Note the , It's just painting the lily and using it indiscriminately .


About affairs

A transaction is logically a set of operations , Or execute , Or not to carry out . Mainly for databases , for instance MySQL.

Just remember that , It's easy to understand things . stay Java in , We usually deal with multiple events in the business , For example, programming meow has a way to save articles , In addition to saving the article itself , Also save the tag corresponding to the article , Labels and articles are not in the same table , But will pass in the article table (posts) Save tag primary key (tag_id) To associate label tables (tags):

public void savePosts(PostsParam postsParam) {
 //  Save the article 
 save(posts);
 //  Handle labels 
  insertOrUpdateTag(postsParam, posts);
}

Then you need to start the transaction , Ensure that the data in the article table and the label table are kept in sync , Or both , Either not .

Otherwise, it may cause , The article was saved successfully , But the label save failed , Or the article failed to save , Label saved successfully —— None of these scenarios met our expectations .

To ensure that transactions are correct and reliable , When the database is being written or updated , You have to show ACID Of 4 There are two important characteristics :

  • Atomicity (Atomicity): All operations in a transaction , Or it's all done , Or not at all , It doesn't end in the middle . An error occurred during the execution of the transaction , Will be rolled back (Rollback) Status to the beginning of the transaction , It's like this transaction has never been executed .

  • Uniformity (Consistency): Before and after transaction start , The integrity of the database is not compromised .

  • The transaction isolation (Isolation): The database allows multiple concurrent transactions to read, write and modify its data at the same time , Isolation can prevent data inconsistency caused by cross execution when multiple transactions are executed concurrently .

  • persistence (Durability): After transaction ends , Changes to data are permanent , Even if the system fails, it will not be lost .

among , Transaction isolation is divided into 4 There are two different levels , Include :

  • Uncommitted read (Read uncommitted), Lowest isolation level , allow “ Dirty reading ”(dirty reads), Transactions can see other transactions “ Not yet submitted ” Modification of . If another transaction rolls back , So the data read by the current transaction is dirty data .

  • Submit to read (read committed), A transaction may encounter a non repeatable read (Non Repeatable Read) The problem of . Nonrepeatable reading means , In a business , Read the same data multiple times , Before the end of the business , If another transaction happens to modify this data , that , In the first transaction , The data read twice may be inconsistent .

  • Repeatable (repeatable read), A transaction may encounter unreal reading (Phantom Read) The problem of . Unreal reading means , In a transaction , The first time to query a record , Found no , however , When trying to update this nonexistent record , To succeed , also , Read the same record again , It magically appears .

  • Serialization (Serializable), The strictest level of isolation , All transactions are executed in order , therefore , Dirty reading 、 It can't be read repeatedly 、 Unreal reading doesn't show up . although Serializable Transactions at the isolation level have the highest security , however , Because the transaction is serial execution , So the efficiency will be greatly reduced , The performance of the application will degrade dramatically . If there is no particularly important situation , I don't usually use Serializable Isolation level .

What needs special attention is : Whether the transaction can take effect , Depends on whether the database engine supports transactions ,MySQL Of InnoDB The engine supports transactions , but MyISAM I don't support .

About Spring Support for transactions

Spring Two transaction modes are supported , They are programmatic transactions and declarative transactions , The latter is most common , Usually only one is required  @Transactional  Just like the ( Code intrusion is minimized ), Just like this. :

@Transactional
public void savePosts(PostsParam postsParam) {
 //  Save the article 
 save(posts);
 //  Handle labels 
  insertOrUpdateTag(postsParam, posts);
}

1) Programming transactions

Programming a transaction means embedding the transaction management code into the business code , To control transaction commit and rollback .

Do you like , Use TransactionTemplate To manage things :

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....   Business code 
                } catch (Exception e){
                    // Roll back 
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}

Again for instance , Use TransactionManager To manage things :

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....   Business code 
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

In terms of programmatic transaction management ,Spring More recommended TransactionTemplate.

In programmatic transactions , Additional transaction management code must be included in each business operation , As a result, the code looks very bloated , But understanding Spring Our transaction management model is very helpful .

2) Declarative transactions

Declarative transactions separate transaction management code from business methods , Implement transaction management declaratively , For developers , Declarative transactions are obviously easier to use than programmatic transactions 、 Better to use .

Yes, of course , To realize the separation of transaction management and business code , You have to use Spring One of the most critical and core technologies ,AOP, Its essence is to intercept before and after the method , Then create or join a transaction before the target method starts , After the target method is executed, submit or roll back according to the execution status .

Declarative transactions are better than programmatic transactions , But there are also shortcomings , The granularity of declarative transaction management is method level , Programming transactions can be accurate to the code block level .

Transaction management model

Spring The core of transaction management is abstracted as a transaction manager (TransactionManager), Its source code has only a simple interface definition , Belongs to a tag interface :

public interface TransactionManager {

}

This interface has two sub interfaces , They are programmatic transaction interfaces ReactiveTransactionManager And declarative transaction interface PlatformTransactionManager. Let's focus on PlatformTransactionManager, This interface defines 3 Interface methods :

interface PlatformTransactionManager extends TransactionManager{
    //  Get the transaction status according to the transaction definition 
    TransactionStatus getTransaction(TransactionDefinition definition)
            throws TransactionException;

    //  Commit transaction 
    void commit(TransactionStatus status) throws TransactionException;

    //  Transaction rollback 
    void rollback(TransactionStatus status) throws TransactionException;
}

adopt PlatformTransactionManager This interface ,Spring For various platforms such as JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager) The corresponding transaction manager is provided , But the specific implementation is the business of each platform .

Parameters TransactionDefinition and @Transactional Annotations correspond to , for instance @Transactional The transaction propagation behavior defined in the annotation 、 Isolation level 、 Transaction timeout 、 Whether the transaction is read-only , stay TransactionDefinition Can be found .

Return type TransactionStatus It is mainly used to store some states and data of the current transaction , For example, transaction resources (connection)、 Rollback status, etc .

TransactionDefinition.java:

public interface TransactionDefinition {

 //  The spread of transactions 
 default int getPropagationBehavior() {
  return PROPAGATION_REQUIRED;
 }

 //  The isolation level of the transaction 
 default int getIsolationLevel() {
  return ISOLATION_DEFAULT;
 }

  //  Transaction timeout 
  default int getTimeout() {
  return TIMEOUT_DEFAULT;
 }

  //  Whether the transaction is read-only 
  default boolean isReadOnly() {
  return false;
 }
}

Transactional.java

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

 Propagation propagation() default Propagation.REQUIRED;
 Isolation isolation() default Isolation.DEFAULT;
  int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
  boolean readOnly() default false;

}
  • @Transactional In the annotations propagation Corresponding TransactionDefinition Medium getPropagationBehavior, The default value is  Propagation.REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED).

  • @Transactional In the annotations isolation Corresponding TransactionDefinition Medium getIsolationLevel, The default value is  DEFAULT(TransactionDefinition.ISOLATION_DEFAULT).

  • @Transactional In the annotations timeout Corresponding TransactionDefinition Medium getTimeout, The default value is TransactionDefinition.TIMEOUT_DEFAULT.

  • @Transactional In the annotations readOnly Corresponding TransactionDefinition Medium isReadOnly, The default value is false.

Speaking of this , Let's elaborate on Spring The spread of transactions 、 The isolation level of the transaction 、 Transaction timeout 、 Read only attribute of transaction , And rollback rules for transactions .

Transaction propagation behavior

When a transaction method is called by another transaction method , You must specify how transactions should propagate , for example , Method may continue to execute in the current transaction , You can also start a new transaction , Execute in your own business .

The propagation of declarative transactions can be done through @Transactional In the annotations propagation Attribute to define , for instance :

@Transactional(propagation = Propagation.REQUIRED)
public void savePosts(PostsParam postsParam) {
}

TransactionDefinition A total definition of 7 Business communication behavior :

01、PROPAGATION_REQUIRED

This is also @Transactional Default transaction propagation behavior , This means that if a transaction currently exists , Then join the transaction ; If there is no current transaction , Create a new transaction . More precisely, it means :

  • If the external method does not open the transaction ,Propagation.REQUIRED Decorated internal methods open their own transactions , And the open transactions are independent of each other , Mutual interference .

  • If the external method starts a transaction and is Propagation.REQUIRED Words , all Propagation.REQUIRED Decorated internal methods and external methods belong to the same transaction , Just one method to roll back , The entire transaction needs to be rolled back .

Class A {
    @Transactional(propagation=Propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();
    }
}

Class B {
    @Transactional(propagation=Propagation.PROPAGATION_REQUIRED)
    public void bMethod {
       //do something
    }
}

This communication behavior is best understood ,aMethod Called bMethod, As long as one of the methods is rolled back , Roll back entire transaction .

02、PROPAGATION_REQUIRES_NEW

Create a new transaction , If there are currently transactions , Suspend the current transaction . That is to say, whether the external method opens the transaction or not ,Propagation.REQUIRES_NEW Decorated internal methods will open their own transactions , And the open transaction is independent of the external transaction , Mutual interference .

Class A {
    @Transactional(propagation=Propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();
    }
}

Class B {
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void bMethod {
       //do something
    }
}

If aMethod() An unexpected rollback occurred ,bMethod() It doesn't roll back , because bMethod() Open up a separate business . however , If bMethod() If an uncapped exception is thrown and the exception satisfies the transaction rollback rule ,aMethod() It also rolls back .

03、PROPAGATION_NESTED

If there are currently transactions , Execute within the current transaction ; otherwise , On execution and PROPAGATION_REQUIRED Similar operation .

04、PROPAGATION_MANDATORY

If there are currently transactions , Then join the transaction ; If there is no current transaction , Throw an exception .

05、PROPAGATION_SUPPORTS

If there are currently transactions , Then join the transaction ; If there is no current transaction , Continue to run in a non transactional manner .

06、PROPAGATION_NOT_SUPPORTED

Run in a non transactional manner , If there are currently transactions , Suspend the current transaction .

07、PROPAGATION_NEVER

Run in a non transactional manner , If there are currently transactions , Throw an exception .

3、4、5、6、7 this 5 Three transaction propagation methods are not commonly used , Understanding can .

Transaction isolation level

We have already known the transaction isolation level of the database , Then understand Spring The level of transaction isolation is much easier .

TransactionDefinition That's a total definition 5 Transaction isolation level :

  • ISOLATION_DEFAULT, Use the database's default isolation level ,MySql The default is REPEATABLE_READ, That's repeatable .

  • ISOLATION_READ_UNCOMMITTED, Lowest isolation level , There may be dirty reading 、 Unreal or unrepeatable .

  • ISOLATION_READ_COMMITTED, Allow reading data committed by concurrent transactions , It can prevent dirty reading , But unreal reading and unrepeatable reading can still happen .

  • ISOLATION_REPEATABLE_READ, Multiple reads of the same field are consistent , Unless the data is modified by its own transaction , Can prevent dirty and unrepeatable read , But phantom reading can still happen .

  • ISOLATION_SERIALIZABLE, Highest isolation level , Although it can prevent dirty reading 、 Unreal and unrepeatable reading , But it will seriously affect the program performance .

Usually , We use the default isolation level ISOLATION_DEFAULT That's all right. , That is, it is left to the database to decide , Can pass  SELECT @@transaction_isolation;  Order to see MySql The default isolation level of , The result is REPEATABLE-READ, That's repeatable .

dd0a9e84754970bf938832759e75e7fe.png

Transaction timeout

Transaction Timeout , That is, the maximum time allowed for a transaction to be executed , If it has not been completed within the timeout period , Automatically rollback .

If the execution time of a transaction is extraordinarily long , Because the transaction involves locking the database , This will cause long-running transactions to consume database resources .

Read only attribute of transaction

If a transaction only reads from the database , Then the database can take advantage of the read-only attribute of the transaction , Take optimization measures , It is applicable to multiple database query operations .

Why should transaction support be enabled for a query operation ?

This is because MySql(innodb) Each connection is enabled by default autocommit Pattern , In this mode , Every one sent to MySql Server's SQL Statements are processed in a separate transaction , After execution, the transaction will be committed automatically .

So if we add @Transactional annotation , So all of this method SQL Will be put in one transaction . otherwise , Every one of them SQL Will start a transaction separately , The data is modified by other transactions , Will read in real time .

In some cases , When multiple query statements are executed at once , When data consistency needs to be ensured , You need to enable transaction support . Otherwise, the previous one SQL After query , Data changed by other users , So next SQL Queries may have inconsistent states .

Rollback strategy of transaction

By default , Transaction exception only at run time (Runtime Exception) Roll back on , as well as Error, A check exception occurred (checked exception, Active capture processing or upward throwing is required ) Do not roll back .

https://tobebetterjavaer.com/exception/gailan.html

If you want to rollback a specific exception type , You can set it like this :

@Transactional(rollbackFor= MyException.class)

About Spring Boot Support for transactions

before , We need to pass XML To configure Spring To host transactions , With Spring Boot after , Everything becomes easier , Just add transaction annotations in the business layer (@Transactional) You can quickly start a transaction .

in other words , We just need to focus on  @Transactional  Just annotate it .

@Transactional The scope of action of

  • Class , Indicates all... In the class public Methods enable transactions

  • On the way , The most commonly used one

  • On the interface , It is not recommended to use

@Transactional Common configuration parameters

although @Transactional Many attributes are defined in the annotation source code , But most of the time , I use the default configuration , Yes, of course , If you need to customize , It has been explained above .

@Transactional A summary of the precautions in the use of

1) To be in public Methodically used , stay AbstractFallbackTransactionAttributeSource Class computeTransactionAttribute There is a judgment in the method , If the target method is not public, be TransactionAttribute return null, That is, transactions are not supported .

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }

    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    // First try is the method in the target class.
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
      return txAttr;
    }

    // Second try is the transaction attribute on the target class.
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
    }

    if (specificMethod != method) {
      // Fallback is to look at the original method.
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
        return txAttr;
      }
      // Last fallback is the class of the original method.
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
      }
    }
    return null;
  }

2) Avoid calling in the same class @Transactional Method of annotation , This will cause the transaction to fail .

More transaction invalidation scenarios

Test whether the transaction is effective

Before the test , Let's put the Spring Boot Default log level info Adjusted for debug, stay application.yml In file modify :

logging:
  level:
    org:
      hibernate: debug
      springframework:
        web: debug

then , Look at the data found before modification :

ee7fd1e330cd6fcf18150b1ee0e2a8b0.png

Open up . Add a... To the controller update Interface , Ready to modify the data , I plan to change the dogleg of silent King II into that of silent King II :

@RequestMapping("/update")
public String update(Model model) {
    User user = userService.findById(2);
    user.setName(" The dog leg of silent King II ");
    userService.update(user);
    return "update";
}

stay Service Add... To the method in  @Transactional  Annotate and throw a runtime exception :

@Override
@Transactional
public void update(User user) {
    userRepository.save(user);
    throw new RuntimeException(" ah , There are monsters !");
}

As we expected , When executed save After saving data , Because there's an anomaly , So the transaction should be rolled back . So the data will not be modified .

Enter... In the browser  http://localhost:8080/user/update  To test , Pay attention to the log , It can be confirmed that the transaction works .

ae3e22458f6868e5beba113dd576fd53.png

When we get rid of things , Also throw an exception :

@Override
public void update(User user) {
    userRepository.save(user);
    throw new RuntimeException(" ah , There are monsters !");
}

Re execution , I found that although the program reported an error , But the data has been updated .

edb8389f22ef51cae621750ed4a152f5.png

This also indirectly proves , our  @Transactional  Things work .

See this , Do you understand why the optimization of new colleagues is just adding to the icing on the cake / Let's use the eggs ?

Project source code

  • Programming meow :https://github.com/itwanger/coding-more

  • Source code of this project :https://github.com/itwanger/codingmore-learning

Reference source :

  • Wikipedia :https://zh.wikipedia.org/wiki/ACID

  • Wikipedia :https://zh.wikipedia.org/wiki/ Transaction isolation

  • Liao Xuefeng :https://www.liaoxuefeng.com/wiki/1177760294764384/1179611198786848

  • JavaGuide:https://juejin.cn/post/6844903608224333838

  • Xiaohui, the whole dish Engineer :https://aijishu.com/a/1060000000013284

  • unreality :https://segmentfault.com/a/1190000040130617

  • A sock :https://www.jianshu.com/p/380a9d980ca5

原网站

版权声明
本文为[Programmer Xiaohui]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/172/202206210938264093.html