1. 程式人生 > >Spring Boot (2)--連線資料庫(mysql)

Spring Boot (2)--連線資料庫(mysql)

本章內容

  • 日誌輸出
  • 連線mysql及使用JPA
  • 使用alibaba.druid
  • 開啟事務

 

日誌輸出

        在Spring Boot中預設使用的是slf4j+logback,並且spring boot規定配置檔案的命名格式最好是xxx-spring.[xml,yml,properties],這樣可以讓boot來管理你的配置檔案。但如果你堅持自定義命名,boot也可以幫你實現,通過在application.yml裡配置logging.config=classpath:mylog-config.xml即可

基本配置

主要包含如下幾個元素:

  • logging.file,日誌檔名稱,如果沒有配置,預設就是專案的目錄下生成一個spring.log的日誌檔案,預設情況下是10MB切分一個檔案。
  • logging.path,日誌目錄,可以是絕對路徑,也可騍相對路徑,例如:/var/project/logs。
  • logging.level,配置日誌級別,配置粒度可以到包級別,例如:logging.level.com.xps=INFO意思就是com.xps包下的所有類的日誌輸出級別都是INFO。logging.level是日誌級別的字首。LEVEL選項包括:TRACE<DEBUG<INFO<WARN<ERROR<FATAL

如上,可以簡單的輸出日誌資訊,但如果我們想定義更為複雜的日誌,例如我們要自定義日誌格式並且不同的環境需要不同的輸出內容等等,那麼,我們就需要有一個logback-spring.xml的日誌配置檔案了。

logback日誌檔案格式

根節點是<configuration>,有以下幾個屬性:

  • scan:預設為true,當為true時,配置檔案如果發生改變,將會被重新載入。
  • scanPeriod:監測配置檔案是否有修改的時間間隔,如果沒有給出時間單位,預設為毫秒。當scan為true時生效。預設為1分鐘。
  • debug:預設為false,當為true時將打印出logback內部日誌資訊,實時檢視logback執行狀態。

此外,它還有五個子節點

節點一<root>

必選節點,用來指定最基礎的日誌輸出級別,有level屬性,預設DEBUG。其它級別還有:TRACE,DEBUG,INFO,WARN,ERROR,ALL,OFF。其可以包含多個appender元素。例如:

<root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="fileInfoLog"/>
    </root>

節點二<contextName>

logger上下文,預設的名稱是"default",可以使用此節點設定其它名稱,用於區分不同應用程式的記錄。一旦設定不能修改,使用時,可以通過%contextName來列印日誌上下文名稱。(此屬性不是必須的,可有可無,所以我們一般都不設定它)。

<contextName>my-project</contextName>

節點三<property>

主要用來定義屬性鍵值對的,有name和value兩個屬性,通過定義的值可以插入到logger上下文中。定義的變數可以使用${}來獲取變數。如果value的值通過application.yml傳過來,使用springProperty。

<property name="logback.appName" value="app"/>

或者使用:

<springProperty scope="context" name="logAppName" source="log.app-name"/>

節點四<appender>

用來格式化日誌輸出,有倆個屬性name和class,class用來指定哪種輸出策略,常用的是控制檯輸出和檔案輸出。

控制檯輸出

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="30 seconds">
 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] [%class:%line] - %m %n</pattern>
        </encoder>
    </appender>
    <appender name="STDOUT1" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %contextName [%level] [%class:%line] - %m %n</pattern>
        </layout>
    </appender>
  <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="STDOUT1"/>
    </root>
<configuration>

可以看到有encoder和layout兩種標籤,它們都可以將事件輪換成格式化的日誌,但是一般控制檯輸出使用layout,檔案輸出使用encoder。

輸出到檔案

隨著專案的執行時間增長,日誌檔案也越來越大,所以需要把日誌檔案切分成多個檔案以便於讀取分析或複製儲存。RollingFileAppender就是用來切分日誌檔案的:

<appender name="infoLogToFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--如果只是想要 Info 級別的日誌,只是過濾 info 還是會輸出 Error 日誌,因為 Error 的級別高,所以我們使用下面的策略,可以避免輸出 Error 的日誌-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--過濾 Error-->
            <level>ERROR</level>
            <!--匹配到就禁止-->
            <onMatch>DENY</onMatch>
            <!--沒有匹配到就允許-->
            <onMismatch>ACCEPT</onMismatch>
        </filter>
    <!--日誌名稱,如果沒有File 屬性,那麼只會使用FileNamePattern的檔案路徑規則
        如果同時有<File>和<FileNamePattern>,那麼當天日誌是<File>,明天會自動把今天
        的日誌改名為今天的日期。即,<File> 的日誌都是當天的。
    -->
        <File>${logPath}/info.${logAppName}.log</File>
        <!--滾動策略,按照時間滾動 TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--檔案路徑,定義了日誌的切分方式——把每一天的日誌歸檔到一個檔案中,以防止日誌填滿整個磁碟空間-->
            <FileNamePattern>${logPath}/info.${logAppName}.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--只保留最近90天的日誌-->
            <maxHistory>90</maxHistory>
            <!--用來指定日誌檔案的上限大小,那麼到了這個值,就會刪除舊的日誌-->
            <!--<totalSizeCap>1GB</totalSizeCap>-->
        </rollingPolicy>
        <!--日誌輸出編碼格式化-->
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
        </encoder>
    </appender>

<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--如果只是想要 Error 級別的日誌,那麼需要過濾一下,預設是 info 級別的,ThresholdFilter-->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>Error</level>
    </filter>
    <!--日誌名稱,如果沒有File 屬性,那麼只會使用FileNamePattern的檔案路徑規則
        如果同時有<File>和<FileNamePattern>,那麼當天日誌是<File>,明天會自動把今天
        的日誌改名為今天的日期。即,<File> 的日誌都是當天的。
    -->
    <File>${logPath}/error.${logAppName}.log</File>
    <!--滾動策略,按照時間滾動 TimeBasedRollingPolicy-->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--檔案路徑,定義了日誌的切分方式——把每一天的日誌歸檔到一個檔案中,以防止日誌填滿整個磁碟空間-->
        <FileNamePattern>${logPath}/error.${logAppName}.%d{yyyy-MM-dd}.log</FileNamePattern>
        <!--只保留最近90天的日誌-->
        <maxHistory>90</maxHistory>
        <!--用來指定日誌檔案的上限大小,那麼到了這個值,就會刪除舊的日誌-->
        <!--<totalSizeCap>1GB</totalSizeCap>-->
    </rollingPolicy>
    <!--日誌輸出編碼格式化-->
    <encoder>
        <charset>UTF-8</charset>
        <pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
    </encoder>
</appender>

節點五<logger>

root節點的子類,指定包下的類輸出日誌的級別,它包括了兩個屬性:

  • level:日誌級別,如果未指定則繼承root的level
  • addtivity:是否將logger的列印資訊向上級傳遞,預設為true

除此之外,它還可以包含<appender>子節點。可以有多個logger節點,如果有包重疊的話以範圍小的生效。

例如:

<logger name="com.xps.sc.springbootdemo" level="DEBUG" additivity="false">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="STDOUT1"/>
</logger>

多環境日誌

在做專案時,我們都會有開發環境,測試環境,生產環境這樣的劃分,只需要指定環境變數即可切換不同的環境。然後在,application.yml中配置spring.profiles.active=dev來切換

<springProfile name="dev,test"><!-- 多個環境可以使用逗號隔開. -->
    <logger name="com.xps.sc.springbootdemo" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="fileInfoLog"/>
        <appender-ref ref="fileErrorLog"/>
    </logger>
</springProfile>

連線資料庫及JPA

我們以mysql+jpa為例,springboot連線資料庫還是比較簡單的,直接在application.yml中配置即可(或各環境的yml檔案中)。

先引入相關的jar包:

<!--JPA連線-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--mysql資料庫驅動-->
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--querydsl依賴,這個不是jpa必須的,但如果要使用jpa的高階功能就需要此包-->
<dependency>
   <groupId>com.querydsl</groupId>
   <artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
   <groupId>com.querydsl</groupId>
   <artifactId>querydsl-apt</artifactId>
   <scope>provided</scope>
</dependency>

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.1.3</version>
</dependency>

資料庫連線的配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    platform: mysql
    url: jdbc:mysql://localhost:3306/jsc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
  jpa:
   properties:
    hibernate:
      hbm2ddl:
        auto: update
      dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  show-sql: true

這樣就配置好了,但啟動後可能你會發現一個小錯誤(但並不影響專案執行):

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is 
`com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and 
manual loading of the driver class is generally unnecessary.

是 說com.mysql.jdbc.Driver已經不使用了,而改為com.mysql.cj.jdbc.Driver,那我們就改成後者即可。

新增dao包,新建UserDao類並繼承JpaRepository。

public interface UserDao extends JpaRepository<User,Long> {

    User queryByUserName(String userName);

    User findByUserNameOrEmail(String userName,String email);

}

JPA是一個ORM規範的框架,基本的CRUD操作在JpaRepository類中已經定義,直接拿來使用即可。上類中的兩個方法是jpa的另一個比較方便的特點,就是根據方法名稱來生成sql,例如,上面的findByUserNameOrEmail就是使用userName或email查詢,其生成的sql,類似於:

SELECT 
  user0_.user_id AS user_id1_0_,
  user0_.create_time AS create_t2_0_,
  user0_.email AS email3_0_,
  user0_.nick_name AS nick_nam4_0_,
  user0_.pass_word AS pass_wor5_0_,
  user0_.reg_time AS reg_time6_0_,
  user0_.user_name AS user_nam7_0_ 
FROM
  t_test_user user0_ 
WHERE user0_.user_name = 'aaa' or user0_.email=''

總體上來說,目前操作SQL主要有兩種形式,一種是面向物件不寫sql,根據配置生成相關的sql操作,像jpa,Hibernate等等,另一種是可以配置sql的用的比較多的就是mybatis了。關於jpa的其它特性請自行參考:https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/

 

使用druid

druid是一個可以監控資料庫連線及sql的專案,我們可以引入此專案來監控資料庫連線等。

相關的配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    driver-class-name: com.mysql.cj.jdbc.Driver
    platform: mysql
    url: jdbc:mysql://localhost:3306/jsc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    filters: stat,wall
    logSlowSql: true
#自定義druid需要的相關引數
druid:
  console:
    urlMapping: '/druid/*'
    login-username: admin
    login-password: admin
  url-pattern: '/*'
  exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'

然後,寫一個配置類來載入這些內容

@Configuration
public class DruidConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(DruidConfiguration.class);

    private static final String DB_PREFIX = "spring.datasource";

    @Value("${druid.console.urlMapping}")
    private String urlMapping;
    @Value("${druid.console.login-username}")
    private String loginUserName;
    @Value("${druid.console.login-password}")
    private String loginPassword;

    @Value("${druid.url-pattern}")
    private String urlPattern;
    @Value("${druid.exclusions}")
    private String exclusions;

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean =
                new ServletRegistrationBean(new StatViewServlet(), urlMapping);
        // IP白名單
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // IP黑名單(共同存在時,deny優先於allow)
        servletRegistrationBean.addInitParameter("deny", "192.168.1.22");
        //控制檯管理使用者
        servletRegistrationBean.addInitParameter("loginUsername",loginUserName);
        servletRegistrationBean.addInitParameter("loginPassword",loginPassword);
        //是否能夠重置資料 禁用HTML頁面上的“Reset All”功能
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns(urlPattern);
        filterRegistrationBean.addInitParameter("exclusions", exclusions);

        return filterRegistrationBean;
    }

    //解決 spring.datasource.filters=stat,wall無法正常註冊進去
    @Component
    @ConfigurationProperties(prefix = DB_PREFIX)
    @Getter
    @Setter //lombok包只的標籤,可以省略寫get,set方法
    class IDataSourceProperties {
        private String url;
        private String username;
        private String password;
        private String driverClassName;
        private int initialSize;
        private int minIdle;
        private int maxActive;
        private int maxWait;
        private int timeBetweenEvictionRunsMillis;
        private int minEvictableIdleTimeMillis;
        private String validationQuery;
        private boolean testWhileIdle;
        private boolean testOnBorrow;
        private boolean testOnReturn;
        private boolean poolPreparedStatements;
        private int maxPoolPreparedStatementPerConnectionSize;
        private String filters;
        private String connectionProperties;

        @Bean 
    @Primary  //在同樣的DataSource中,首先使用被標註的DataSource
        public DataSource dataSource() {
            DruidDataSource datasource = new DruidDataSource();
            datasource.setUrl(url);
            datasource.setUsername(username);
            datasource.setPassword(password);
            datasource.setDriverClassName(driverClassName);
            datasource.setInitialSize(initialSize);
            datasource.setMinIdle(minIdle);
            datasource.setMaxActive(maxActive);
            datasource.setMaxWait(maxWait);
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            datasource.setValidationQuery(validationQuery);
            datasource.setTestWhileIdle(testWhileIdle);
            datasource.setTestOnBorrow(testOnBorrow);
            datasource.setTestOnReturn(testOnReturn);
            datasource.setPoolPreparedStatements(poolPreparedStatements);
            datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
            try {
                datasource.setFilters(filters);
            } catch (SQLException e) {
                logger.error("druid configuration initialization filter: " + e);
            }
            datasource.setConnectionProperties(connectionProperties);
            return datasource;
        }
    }

    public String getLoginUserName() {
        return loginUserName;
    }

    public void setLoginUserName(String loginUserName) {
        this.loginUserName = loginUserName;
    }

    public String getLoginPassword() {
        return loginPassword;
    }

    public void setLoginPassword(String loginPassword) {
        this.loginPassword = loginPassword;
    }

    public String getUrlMapping() {
        return urlMapping;
    }

    public void setUrlMapping(String urlMapping) {
        this.urlMapping = urlMapping;
    }

    public String getUrlPattern() {
        return urlPattern;
    }

    public void setUrlPattern(String urlPattern) {
        this.urlPattern = urlPattern;
    }

    public String getExclusions() {
        return exclusions;
    }

    public void setExclusions(String exclusions) {
        this.exclusions = exclusions;
    }
}

OK,到此已經配置完成了,是不是很簡單呢。然後我們就可以在瀏覽器中訪問:

http://localhost:8000/druid/login.html就會出現

 

輸入我們配置的loginuserName和password就可以了,進來之後就可以看到

 

事務配置

        在做專案中,事務也分為分散式事務和單庫事務,分散式事務這個比較複雜再這裡不再闡述,單庫事務就是在同一個資料庫中的事務,spring boot已經為我們注入的宣告式事務機制。只需要我們在業務方法上使用@Transactional即可支援事務。例如:

@Override
@Transactional
public User modify(User user) {
    if(user==null)
        user = new User();
    user.setUserName("ddd");
    user.setPassWord("ddd");
    user.setEmail("[email protected]");
    user.setNickName("王老d");
    user.setRegTime("2018-12-19 11:19:10");
    user.setCreateTime(new Date());
    userDao.save(user);
//會丟擲異常,觀察資料庫中有沒有把ddd儲存成功,如果沒有儲存成功,那就說明有事務加持。
    int a = Integer.parseInt("a");

    user = new User();
    user.setUserName("ccc");
    user.setPassWord("ccc");
    user.setEmail("[email protected]163.com");
    user.setNickName("王老七");
    user.setRegTime("2018-12-19 11:19:10");
    user.setCreateTime(new Date());
    userDao.save(user);
    return user;
}

另外,我們也可以通過檢視日誌來判斷有無事務參與。

[DEBUG] [org.springframework.core.log.LogFormatUtils:90] - GET "/modifyUser/3", parameters={}

2018-12-21 15:10:30 [DEBUG] [org.springframework.web.servlet.handler.AbstractHandlerMapping:420] - Mapped to public com.xps.sc.springbootdemo.entity.User com.xps.sc.springbootdemo.controller.HelloBootController.modifyUser(java.lang.Long)

2018-12-21 15:10:30 [DEBUG] [org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor:86] - Opening JPA EntityManager in OpenEntityManagerInViewInterceptor

2018-12-21 15:10:30 [DEBUG] [org.springframework.orm.jpa.JpaTransactionManager:355] - Found thread-bound EntityManager [SessionImpl(1299376334<open>)] for JPA transaction

2018-12-21 15:10:30 [DEBUG] [org.springframework.transaction.support.AbstractPlatformTransactionManager:372] - Creating new transaction with name [com.xps.sc.springbootdemo.service.impl.UserServiceImpl.modify2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

2018-12-21 15:10:30 [DEBUG] [org.hibernate.engine.transaction.internal.TransactionImpl:56] - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false

2018-12-21 15:10:30 [DEBUG] [org.hibernate.engine.transaction.internal.TransactionImpl:78] - begin

2018-12-21 15:10:30 [DEBUG] [org.springframework.orm.jpa.JpaTransactionManager:419] - Exposing JPA transaction as JDBC [org.springframewo[email protected]60fad735]

2018-12-21 15:10:30 [DEBUG] [org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$AbstractFallbackTransactionAttributeSource:355] - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

2018-12-21 15:10:30 [DEBUG] [org.springframework.orm.jpa.JpaTransactionManager:355] - Found thread-bound EntityManager [SessionImpl(1299376334<open>)] for JPA transaction

2018-12-21 15:10:30 [DEBUG] [org.springframework.transaction.support.AbstractPlatformTransactionManager:473] - Participating in existing transaction

2018-12-21 15:10:30 [DEBUG] [org.springframework.beans.CachedIntrospectionResults:186] - Not strongly caching class [com.xps.sc.springbootdemo.entity.User] because it is not cache-safe

2018-12-21 15:10:30 [DEBUG] [org.hibernate.engine.jdbc.spi.SqlStatementLogger:94] - select next_val as id_val from hibernate_sequence for update

Hibernate: select next_val as id_val from hibernate_sequence for update

2018-12-21 15:10:30 [DEBUG] [org.hibernate.engine.jdbc.spi.SqlStatementLogger:94] - update hibernate_sequence set next_val= ? where next_val=?

Hibernate: update hibernate_sequence set next_val= ? where next_val=?

2018-12-21 15:10:30 [DEBUG] [org.hibernate.event.internal.AbstractSaveEventListener:136] - Generated identifier: 25, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator

2018-12-21 15:10:31 [DEBUG] [org.springframework.transaction.support.AbstractPlatformTransactionManager:836] - Initiating transaction rollback

2018-12-21 15:10:31 [DEBUG] [org.springframework.orm.jpa.JpaTransactionManager:553] - Rolling back JPA transaction on EntityManager [SessionImpl(1299376334<open>)]

2018-12-21 15:10:31 [DEBUG] [org.hibernate.engine.transaction.internal.TransactionImpl:136] - rolling back

2018-12-21 15:10:31 [DEBUG] [org.springframework.orm.jpa.JpaTransactionManager:623] - Not closing pre-bound JPA EntityManager after transaction

2018-12-21 15:10:31 [ERROR] [com.xps.sc.springbootdemo.controller.HelloBootController:64] - HelloBootController modify is error

另一種事務配置方式是全域性性事務,不需要我們標註@Transactional也可以實現事務,和以前使用spring的aop實現的事務控制一樣,在spring boot中也可以實現。

首先,我們需要引入aop包的支援:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然後,與druid配置類似,也需要有一個配置類來設定aop一些屬性,不用解釋,相信大家也能看懂。與以前使用spring的xml配置元素一樣

@Aspect
@Configuration
public class TransactionAdviceConfig {
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.xps.sc.springbootdemo.service.*.*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;//會自動注入,
//提示錯誤不用管,如果使用的是jpa則會自動注入成
//JpaPlatformTransactionManager

    @Bean
    public TransactionInterceptor txAdvice() {

        DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
        txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
        txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        txAttr_REQUIRED_READONLY.setReadOnly(true);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        source.addTransactionalMethod("add*", txAttr_REQUIRED);
        source.addTransactionalMethod("save*", txAttr_REQUIRED);
        source.addTransactionalMethod("delete*", txAttr_REQUIRED);
        source.addTransactionalMethod("update*", txAttr_REQUIRED);
        source.addTransactionalMethod("modify*", txAttr_REQUIRED);
        source.addTransactionalMethod("exec*", txAttr_REQUIRED);
        source.addTransactionalMethod("set*", txAttr_REQUIRED);
        source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("*", txAttr_REQUIRED);
        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

然後同樣使用上述方法不加@Transactional測試,效果一樣。

總結:

      本章主要是介紹了spring boot與mysql資料庫,jpa的結合使用。也說了一些事務配置。這也是我們做一般專案必須使用的。所以以此筆記梳理記憶。接下來,spring boot還可以結合mybatis框架,redis快取及MQ訊息中介軟體等等。努力學習ing。

參考:https://blog.csdn.net/inke88/article/details/75007649

http://www.ityouknow.com/springboot/2016/08/20/spring-boo-jpa.html