Skip to content

SpringBoot DB

Baoying Wang edited this page Feb 19, 2019 · 2 revisions

Introduction

DB connection setup

Just follow the common practice to setup the related url/username/pwd/driver etc.

spring.datasource.url=jdbc:mysql://localhost:3306/xxx_hub?useSSL=false&characterEncoding=utf8
spring.datasource.username=xxx_u
spring.datasource.password=yyy1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

DB Transaction

  1. enable db tx by adding annotation @EnableTransactionManagement to your configuration class or main application class
  2. add @Transactional to the method where you expect transaction. It supports adjusts default behaviors to set the parameters, e.g. @Transactional(RollbackFor=Exception.class). See related document.

Special note: your tx maybe does work if the @Transactional is added on a method which is NOT invoked by a auto wired bean. see:https://stackoverflow.com/questions/37310550/spring-transaction-management-not-working-with-spring-boot-mybatis @Transactional只有在Autowired bean直接调用才行(间接调用都不行!!) 如果你的事物注解在方法上不生效,而在类上就生效,则一定是这问题! 上面这个链接讲的比较清楚。我下面给个间接调用不生效的例子,方便我们理解。

Here is an example

@Component
class App{
    @Autowired X x
    public void doSomething(){
        x.insertDataDirectly(); // the tx does work
        x.insertDataInDirectly(); //the tx does NOT work, although @Transactional method is used finally. Because the @Transactional method is NOT invoked by proxied bean directly. 
    }
}

@Component
class X{
    @Transactional
    public void insertDataDirectly(){
        db.insert...
    }
    
    //外部调用这个方法的发,事物并不生效,虽然内部的方法添加了@Transactional注解。
    public void insertDataInDirectly(){
        insertDataDirectly();
    }
}

A workaround for above problem. But the workaround is NOT easy to use since it makes code complex. Try to review your business logic and confirm whether you can add @Transactional on the bean and be called directly. 一个比较绕的解决办法.在下面的代码中,因为txWapper是autowired,所以spring proxy能够成功解析到。进而事物能够生效。这个方法我测试过了。但是要重新考虑,能不能把Transactional直接加在最外面的方法上。否则这个解决办法太绕了。

注意-容易形成循环依赖 - X依赖TxWrapper,TxWrapper调用X的方法,则会出现问题(TxWrapper的父引用为null)。别用普通内部类了
@Component
class X{
    
    @Autowired
    private TxWrapper txWrapper;
    
    @Component
    static class TxWrapper{
        
        @Transactional
        public void insertDataDirectly(){
            db.insert...
        }
    }
    
    //外部调用这个方法的发,事物并不生效,虽然内部的方法添加了@Transactional注解。
    public void insertDataInDirectly(){
        txWrapper.insertDataDirectly();
    }
}
  1. 这个Transactional的方法必须是public的(试过了-https://stackoverflow.com/questions/3037006/starting-new-transaction-in-spring-bean)
  2. 另外,一个生效的Transactional注解调用,可以调用多个含有mapper/dao的子调用,事物仍在存在/生效(不用声明另外的Transactional注解)。 相反,如果这些子方法声明了Transactional注解,则还需要查看Propergation的东西。如果业务不太复杂,尽量避免。

工具:如何识别当前方法的事物是否已经开启? http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/ 这个链接提供了一个工具方法,相当好用

//http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/

import java.lang.reflect.InvocationTargetException;

public class DebugUtils {
    private static final boolean transactionDebugging = true;
    private static final boolean verboseTransactionDebugging = true;

    public static void showTransactionStatus(String message) {
        System.out.println(((transactionActive()) ? "[+] " : "[-] ") + message);
    }

    // Some guidance from: http://java.dzone.com/articles/monitoring-declarative-transac?page=0,1
    public static boolean transactionActive() {
        try {
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            Class tsmClass = contextClassLoader.loadClass("org.springframework.transaction.support.TransactionSynchronizationManager");
            Boolean isActive = (Boolean) tsmClass.getMethod("isActualTransactionActive", null).invoke(null, null);

            return isActive;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        // If we got here it means there was an exception
        throw new IllegalStateException("ServerUtils.transactionActive was unable to complete properly");
    }

    public static void transactionRequired(String message) {
        // Are we debugging transactions?
        if (!transactionDebugging) {
            // No, just return
            return;
        }

        // Are we doing verbose transaction debugging?
        if (verboseTransactionDebugging) {
            // Yes, show the status before we get to the possibility of throwing an exception
            showTransactionStatus(message);
        }

        // Is there a transaction active?
        if (!transactionActive()) {
            // No, throw an exception
            throw new IllegalStateException("Transaction required but not active [" + message + "]");
        }
    }
}

Clone this wiki locally