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]");
    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.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@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

猜你喜欢

转载自blog.csdn.net/xpsallwell/article/details/85164712