您的位置:首页 > 编程语言 > Java开发

spring boot学习3之mybatis+druid+事务+PageHelper分页插件+sql打印插件整合

2017-05-20 14:35 597 查看
       上篇博文学习了spring boot下对于配置文件的读取。这篇博文学习下spring boot怎样整合mybatis,并使用指定的数据库连接池,事务,分页插件的整合。

      该例子配置开启事务,所以先说下事务的一点注意事项。

注:虽然在配置类中开启了事务,但是没有指定哪些类哪些方法的执行要开启事务(除非定义个AOP),所以需要自行在service类上面或方法上面@Transactional。在类上面加@Transactional,说明类内的每个public方法都默认使用它,除非方法上自行定义@Transactional覆盖类上的定义。

   如果类或方法名上都没有@Transactional,那么执行了数据库操作后,后续代码抛出了异常,数据库也不会回滚,因为没声明开启事务,数据库默认自动提交了。

    事务嵌套:@Transactional时,默认Propagation.REQUIRED (有事务就处于当前事务中,没事务就创建一个事务),所以如果A01Service.a()调用A01Service.b()同时调用A02Service.c(),同类内部的事务方法或不同类的事务方法,spring处理时,会将它们都置于同一个事务中,也就是要么都失败,要么都成功,不会出现多个事务,一个提交了,另一个回滚了。这个进行框架搭建时,可自行测试。

      本例子用的是maven结构。例子源码下载在github上,点击下载

项目结构



mapper接口和mapper.xml文件,我都是放在dao文件夹下

其中pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.fei.springboot</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>
<!-- 文件拷贝时的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 编译时的编码 -->
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>

<spring_versin>1.5.2.RELEASE</spring_versin>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring_versin}</version>

<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>

</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>${spring_versin}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>

<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>

</dependencies>

<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

application.yml文件

logging:
config: classpath:logback.xml
path: d:/logs
server:
port: 80
session-timeout: 60

mybatis:
mapperLocations: classpath:/com/fei/springboot/dao/*.xml
typeAliasesPackage: com.fei.springboot.dao
mapperScanPackage: com.fei.springboot.dao
configLocation: classpath:/mybatis-config.xml

spring:
datasource:
name: db
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat # ,wall,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据
druidLoginName: wjf # 登录druid的账号
druidPassword: wjf # 登录druid的密码
mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 使全局的映射器启用或禁用缓存。 -->
<setting name="cacheEnabled" value="true" />
<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
<setting name="useColumnLabel" value="true" />
<!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
<setting name="useGeneratedKeys" value="false" />
<!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
<setting name="autoMappingBehavior" value="PARTIAL" />
<!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
<setting name="defaultExecutorType" value="SIMPLE" />

<setting name="defaultStatementTimeout" value="25" />

<setting name="defaultFetchSize" value="100" />

<setting name="safeRowBoundsEnabled" value="false" />
<!-- 使用驼峰命名法转换字段。 -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
<setting name="localCacheScope" value="SESSION" />
<!-- 默认为OTHER,为了解决oracle插入null报错的问题要设置为NULL -->
<setting name="jdbcTypeForNull" value="NULL" />
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
</settings>

</configuration>
配置文件大概就这样

由于数据库连接池用的是druid,所以需要自己写下代码

DatasourceConfig.java代码

package com.fei.springboot.mybatisconfig;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

@Configuration
public class DatasourceConfig {
private Logger logger = LoggerFactory.getLogger(DatasourceConfig.class);

@Value("${spring.datasource.url}")
private String dbUrl;

@Value("${spring.datasource.type}")
private String dbType;

@Value("${spring.datasource.username}")
private String username;

@Value("${spring.datasource.password}")
private String password;

@Value("${spring.datasource.driver-class-name}")
private String driverClassName;

@Value("${spring.datasource.initialSize}")
private int initialSize;

@Value("${spring.datasource.minIdle}")
private int minIdle;

@Value("${spring.datasource.maxActive}")
private int maxActive;

@Value("${spring.datasource.maxWait}")
private int maxWait;

@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;

@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;

@Value("${spring.datasource.validationQuery}")
private String validationQuery;

@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;

@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;

@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;

@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;

@Value("${spring.datasource.filters}")
private String filters;

@Value("${spring.datasource.connectionProperties}")
private String connectionProperties;

@Value("${spring.datasource.useGlobalDataSourceStat}")
private boolean useGlobalDataSourceStat;

@Value("${spring.datasource.druidLoginName}")
private String druidLoginName;

@Value("${spring.datasource.druidPassword}")
private String druidPassword;

@Bean(name="dataSource",destroyMethod = "close", initMethod="init")
@Primary //不要漏了这
public DataSource dataSource(){
DruidDataSource datasource = new DruidDataSource();
try {
datasource.setUrl(this.dbUrl);
datasource.setDbType(dbType);
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.setFilters(filters);
datasource.setConnectionProperties(connectionProperties);
datasource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);
} catch (SQLException e) {
logger.error("druid configuration initialization filter", e);
}
return datasource;
}

///////// 下面是druid 监控访问的设置 /////////////////
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*"); //url 匹配
reg.addInitParameter("allow", "192.168.16.110,127.0.0.1"); // IP白名单 (没有配置或者为空,则允许所有访问)
reg.addInitParameter("deny", "192.168.16.111"); //IP黑名单 (存在共同时,deny优先于allow)
reg.addInitParameter("loginUsername", this.druidLoginName);//登录名
reg.addInitParameter("loginPassword", this.druidPassword);//登录密码
reg.addInitParameter("resetEnable", "false"); // 禁用HTML页面上的“Reset All”功能
return reg;
}

@Bean(name="druidWebStatFilter")
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //忽略资源
filterRegistrationBean.addInitParameter("profileEnable", "true");
filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
return filterRegistrationBean;
}

}
MybatisConfiguration.java  (注:PageHelper不要使用5.0以上版本,因为5.0以上没实现Interceptor接口,导致SqlSessionFactoryBean添加插件时,失败)

package com.fei.springboot.mybatisconfig;

import java.io.IOException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import com.github.pagehelper.PageHelper;
/**
* mybatis的相关配置设置
* @author Jfei
*
*/
@Configuration
@AutoConfigureAfter(DatasourceConfig.class)
@ConfigurationProperties
@EnableTransactionManagement
//@MapperScan("com.fei.springboot.dao")
public class MybatisConfiguration implements TransactionManagementConfigurer{

private static Log logger = LogFactory.getLog(MybatisConfiguration.class);

//  配置类型别名
@Value("${mybatis.typeAliasesPackage}")
private String typeAliasesPackage;

//  配置mapper的扫描,找到所有的mapper.xml映射文件
//        @Value("${mybatis.mapperLocations : classpath:com/fei/springboot/dao/*.xml}")
@Value("${mybatis.mapperLocations}")
private String mapperLocations;

//  加载全局的配置文件
@Value("${mybatis.configLocation}")
private String configLocation;

@Autowired
private DataSource dataSource;

// 提供SqlSeesion
@Bean(name = "sqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory() {
try {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);

// 读取配置
sessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);

//设置mapper.xml文件所在位置
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocations);
sessionFactoryBean.setMapperLocations(resources);
//设置mybatis-config.xml配置文件位置
sessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));

//添加分页插件、打印sql插件
Interceptor[] plugins = new Interceptor[]{pageHelper(),sqlPrintInterceptor()};
sessionFactoryBean.setPlugins(plugins);

return sessionFactoryBean.getObject();
} catch (IOException e) {
logger.error("mybatis resolver mapper*xml is error",e);
return null;
} catch (Exception e) {
logger.error("mybatis sqlSessionFactoryBean create error",e);
return null;
}
}

@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}

//事务管理
@Bean
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new DataSourceTransactionManager(dataSource);
}

//将要执行的sql进行日志打印(不想拦截,就把这方法注释掉)
@Bean
public SqlPrintInterceptor sqlPrintInterceptor(){
return new SqlPrintInterceptor();
}

/**
* 分页插件
* @param dataSource
* @return
*/

//        <!-- 分页插件 -->
//    	<plugins>
//                    <plugin interceptor="com.github.pagehelper.PageHelper">
//                            <property name="dialect" value="mysql"/>
//                            <!-- 该参数默认为false -->
//    							<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
//    							<!-- 和startPage中的pageNum效果一样 -->
//                            <property name="offsetAsPageNum" value="true"/>
//                             <!-- 该参数默认为false -->
//    							<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
//                            <property name="rowBoundsWithCount" value="true"/>
//                            <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
//    							<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) -->
//                            <property name="pageSizeZero" value="true"/>
//                            <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
//    							<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
//    							<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
//                            <property name="reasonable" value="false"/>
//                            <!-- 支持通过Mapper接口参数来传递分页参数 -->
//                            <property name="supportMethodsArguments" value="false"/>
//                            <!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
//                            <property name="returnPageInfo" value="none"/>
//
//                   </plugin>
//          </plugins>
@Bean
public PageHelper pageHelper() {
PageHelper pageHelper = new PageHelper();
Properties p = new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
p.setProperty("returnPageInfo", "check");
p.setProperty("params", "count=countSql");
pageHelper.setProperties(p);
return pageHelper;
}
}
打印sql拦截插件
SqlPrintInterceptor.java
package com.fei.springboot.mybatisconfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;

/**
* MyBatis 将mybatis要执行的sql拦截打印出来
*
* @since 1.0.0
*/
@Intercepts
({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class SqlPrintInterceptor implements Interceptor {

private static Log logger = LogFactory.getLog(SqlPrintInterceptor.class);

private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameterObject = null;
if (invocation.getArgs().length > 1) {
parameterObject = invocation.getArgs()[1];
}

long start = System.currentTimeMillis();

Object result = invocation.proceed();

String statementId = mappedStatement.getId();
BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
Configuration configuration = mappedStatement.getConfiguration();
String sql = getSql(boundSql, parameterObject, configuration);

long end = System.currentTimeMillis();
long timing = end - start;
if(logger.isInfoEnabled()){
logger.info("执行sql耗时:" + timing + " ms" + " - id:" + statementId + " - Sql:" );
logger.info(" "+sql);
}

return result;
}

@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}

@Override
public void setProperties(Properties properties) {
}

private String getSql(BoundSql boundSql, Object parameterObject, Configuration configuration) {
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
sql = replacePlaceholder(sql, value);
}
}
}
return sql;
}

private String replacePlaceholder(String sql, Object propertyValue) {
String result;
if (propertyValue != null) {
if (propertyValue instanceof String) {
result = "'" + propertyValue + "'";
} else if (propertyValue instanceof Date) {
result = "'" + DATE_FORMAT.format(propertyValue) + "'";
} else {
result = propertyValue.toString();
}
} else {
result = "null";
}
return sql.replaceFirst("\\?", Matcher.quoteReplacement(result));
}
}


按照项目开发习惯,写个dao/service/controller来进行测试下

UserMapper.java

package com.fei.springboot.dao;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.fei.springboot.domain.User;

@Mapper
public interface UserMapper {

@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
void insert(User u);

//注:方法名和要UserMapper.xml中的id一致
List<User> query(@Param("userName")String userName);

@Delete("delete from sys_user")
void deleteAll();
}


UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.fei.springboot.dao.UserMapper">

<select id="query"  resultType="com.fei.springboot.domain.User">
select id ,user_name
from sys_user
where 1=1
<if test="userName != null">
and user_name like CONCAT('%',#{userName},'%')
</if>
</select>
</mapper>
UserService.java

package com.fei.springboot.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.fei.springboot.dao.UserMapper;
import com.fei.springboot.domain.User;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

@Service
@Transactional(readOnly=true,rollbackFor=Exception.class)
public class UserService {

@Autowired
private UserMapper userMapper;

//注意:方法的@Transactional会覆盖类上面声明的事务
//Propagation.REQUIRED :有事务就处于当前事务中,没事务就创建一个事务
//isolation=Isolation.DEFAULT:事务数据库的默认隔离级别
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
public void insertUser(User u){
this.userMapper.insert(u);

//如果类上面没有@Transactional,方法上也没有,哪怕throw new RuntimeException,数据库也会成功插入数据
//	throw new RuntimeException("测试插入事务");
}

public PageInfo<User> queryPage(String userName,int pageNum,int pageSize){
Page<User> page = PageHelper.startPage(pageNum, pageSize);
//PageHelper会自动拦截到下面这查询sql
this.userMapper.query(userName);
return page.toPageInfo();
}

//测试事务
//注意:方法的@Transactional会覆盖类上面声明的事务
//Propagation.REQUIRED :有事务就处于当前事务中,没事务就创建一个事务
//isolation=Isolation.DEFAULT:事务数据库的默认隔离级别
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
public void testTransational(){

//删除全部
this.userMapper.deleteAll();
//新增
User u = new User();
u.setId("123456");
u.setUserName("张三");
this.userMapper.insert(u);
//制造异常
//如果类上面没有@Transactional,方法上也没有,哪怕throw new RuntimeException,数据库也会提交
throw new RuntimeException("事务异常测试");
}

}
 

    注:虽然在配置类中开启了事务,但是没有指定哪些类哪些方法的执行要开启事务,所以需要自行在service类上面或方法上面@Transactional。在类上面加@Transactional,说明类内的每个public方法都默认使用它,除非方法上自行定义@Transactional覆盖类上的定义。

   如果类或方法名上都没有@Transactional,那么执行了数据库操作后,后续代码抛出了异常,数据库也不会回滚,因为没声明开启事务,数据库默认自动提交了。

    事务嵌套:@Transactional时,默认Propagation.REQUIRED (有事务就处于当前事务中,没事务就创建一个事务),所以如果A01Service.a()调用A01Service.b()同时调用A02Service.c(),同类内部的事务方法或不同类的事务方法,spring处理时,会将它们都置于同一个事务中,也就是要么都失败,要么都成功,不会出现多个事务,一个提交了,另一个回滚了。这个进行框架搭建时,可自行测试。

UserController.java

package com.fei.springboot.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fei.springboot.domain.User;
import com.fei.springboot.service.UserService;
import com.github.pagehelper.PageInfo;

@Controller
@RequestMapping("/user")
public class UserController {

@Autowired
private UserService userService;

@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello";
}
/**
* 测试插入
* @return
*/
@RequestMapping("/add")
@ResponseBody
public String add(){
User u = new User();
double i = Math.random()*100;
u.setId(String.valueOf(i));
u.setUserName("test"+i);
this.userService.insertUser(u);
return "success";
}
/**
* 测试分页插件
* @return
*/
@RequestMapping("/queryPage")
@ResponseBody
public String queryPage(){
PageInfo<User> page = this.userService.queryPage("tes", 1, 2);
System.out.println("总页数=" + page.getPages());
System.out.println("总记录数=" + page.getTotal()) ;
for(User u : page.getList()){
System.out.println(u.getId() + " \t " + u.getUserName());
}
return "success";
}
/**
* 测试事务
* @return
*/
@RequestMapping("/testTransational")
@ResponseBody
public String test(){
try {
this.userService.testTransational();
return "success";
} catch (Exception e) {
return e.getMessage();
}

}
}
Application.java

package com.fei.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
@ComponentScan(basePackages={"com.fei.springboot"})
@SpringBootApplication
public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer{

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}

public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
//	configurableEmbeddedServletContainer.setPort(9090);
}
}


执行Application.java,然后浏览器访问controller里的url即可进行测试

比如访问查询时,可以看到日志中打印了sql,并且PageHelper也正常工作

2017-05-20 14:26:03.932  INFO 101936 --- [p-nio-80-exec-2] c.f.s.mybatisconfig.SqlPrintInterceptor  :    select id ,user_name from sys_user where 1=1 and user_name like CONCAT('%','tes','%')
2017-05-20 14:26:03.932 [http-nio-80-exec-2] INFO  c.f.s.m.SqlPrintInterceptor -    select id ,user_name from sys_user where 1=1 and user_name like CONCAT('%','tes','%')
总页数=3
总记录数=5
45.83897583806164 	 test45.83897583806164
5.580134425508676 	 test5.580134425508676


习惯JPA的同学,发现insert对象都需要写sql,会觉得麻烦。那可以把通用Mapper整合进去。通用Mapper比较成熟了,它以整合了jpa,具体可以百度通用mapper,git上作者已经提供spring boot+mybatis+通用mapper的整合demo了

druid 监控

访问 http://127.0.0.1/druid/  输入账号/密码(配置文件那配置的druidLoginName)



完整的例子源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: