在Spring Boot中默认使用的是slf4j+logback,并且spring boot规定配置文件的命名格式最好是xxx-spring.[xml,yml,properties],这样可以让boot来管理你的配置文件。但如果你坚持自定义命名,boot也可以帮你实现,通过在application.yml里配置logging.config=classpath:mylog-config.xml即可
主要包含如下几个元素:
如上,可以简单的输出日志信息,但如果我们想定义更为复杂的日志,例如我们要自定义日志格式并且不同的环境需要不同的输出内容等等,那么,我们就需要有一个logback-spring.xml的日志配置文件了。
根节点是
此外,它还有五个子节点
必选节点,用来指定最基础的日志输出级别,有level属性,默认DEBUG。其它级别还有:TRACE,DEBUG,INFO,WARN,ERROR,ALL,OFF。其可以包含多个appender元素。例如:
logger上下文,默认的名称是"default",可以使用此节点设置其它名称,用于区分不同应用程序的记录。一旦设置不能修改,使用时,可以通过%contextName来打印日志上下文名称。(此属性不是必须的,可有可无,所以我们一般都不设置它)。
my-project
主要用来定义属性键值对的,有name和value两个属性,通过定义的值可以插入到logger上下文中。定义的变量可以使用${}来获取变量。如果value的值通过application.yml传过来,使用springProperty。
或者使用:
用来格式化日志输出,有俩个属性name和class,class用来指定哪种输出策略,常用的是控制台输出和文件输出。
控制台输出:
%d{yyyy-MM-dd HH:mm:ss} [%level] [%class:%line] - %m %n
%d{yyyy-MM-dd HH:mm:ss} %contextName [%level] [%class:%line] - %m %n
可以看到有encoder和layout两种标签,它们都可以将事件轮换成格式化的日志,但是一般控制台输出使用layout,文件输出使用encoder。
输出到文件
随着项目的运行时间增长,日志文件也越来越大,所以需要把日志文件切分成多个文件以便于读取分析或复制保存。RollingFileAppender就是用来切分日志文件的:
ERROR
DENY
ACCEPT
${logPath}/info.${logAppName}.log
${logPath}/info.${logAppName}.%d{yyyy-MM-dd}.log
90
UTF-8
%d [%thread] %-5level %logger{36} %line - %msg%n
Error
${logPath}/error.${logAppName}.log
${logPath}/error.${logAppName}.%d{yyyy-MM-dd}.log
90
UTF-8
%d [%thread] %-5level %logger{36} %line - %msg%n
root节点的子类,指定包下的类输出日志的级别,它包括了两个属性:
除此之外,它还可以包含
例如:
多环境日志
在做项目时,我们都会有开发环境,测试环境,生产环境这样的划分,只需要指定环境变量即可切换不同的环境。然后在,application.yml中配置spring.profiles.active=dev来切换
我们以mysql+jpa为例,springboot连接数据库还是比较简单的,直接在application.yml中配置即可(或各环境的yml文件中)。
先引入相关的jar包:
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
com.querydsl
querydsl-jpa
com.querydsl
querydsl-apt
provided
com.alibaba
druid
1.1.3
数据库连接的配置:
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 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是一个可以监控数据库连接及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
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
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
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包的支持:
org.springframework.boot
spring-boot-starter-aop
然后,与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