目录
小记
这两天比较清闲,对生成器做了进一步的优化与更新,一起来看一下吧!
新增全局事务
相信我们都知道事务在数据操作的过程当中十分重要,举个最简单的例子,如果程序正在处理添加数据的业务,但是程序执行完sql之后的业务流程中产生了异常,这个时候应该回滚,则添加数据无效,保证了业务的统一和数据的完整。一般情况下,使用mysql的时候都是默认使用的innoDB数据库引擎,与MYISAM最大的区别就是加入了事务的支持,我们在平时的开发过程中最常用的也是InnoDB,所以本次更新特地增加了全局的事务支持
先说springboot,增加了transactionAdviceConfig.ftl模板文件,代码如下:
package ${packageName}.config;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
* 全局事务支持
*
* @author zrx
*
*/
@Aspect
@Configuration
public class TransactionAdviceConfig {
private static final String AOP_POINTCUT_EXPRESSION = "execution(* ${packageName}.service.impl.*.*(..))";
@Autowired
private PlatformTransactionManager transactionManager;
@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("delete*", txAttr_REQUIRED);
source.addTransactionalMethod("update*", txAttr_REQUIRED);
source.addTransactionalMethod("select*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("likeSelect*", txAttr_REQUIRED_READONLY);
return new TransactionInterceptor(transactionManager, source);
}
@Bean
public Advisor txAdviceAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
很简单易懂的事务切面,AOP_POINTCUT_EXPRESSION为定义的切入点表达式,这里的${packageName}由freemarker引擎渲染,从而得到正确的属性,生成相应的代码。在这里我把增删改方法的事务传播行为设置为了required,同样也是最常用的,查询方法设置为了readonly,关于事务的传播行为,不明白的朋友可以自行百度,这在平时的开发中还是很重要的一个概念,同样事务的隔离级别也同样需要了解,隔离级别一般设置为read commit(读已提交)即可,当然如果是在高并发的系统中就要考虑数据的一致性和准确性。
举个最简单的例子,用户a在执行a事务,在a表中查询出了一条记录,假设这条记录的某个字段名称也为a,然后继续执行事务。。这个时候b用户去a表中把这条记录的名称更新为b然后提交,但此时a用户的事务还没有结束,后面使用到a表相关的查询,发现字段名称已经不是a而是b了,这就造成了可重复读。解决此类问题的方法也很多,如修改隔离级别,程序加锁等,具体情况需要具体分析。
回到正题,ssm方面增加了mvc-jdbcProperties.ftl,mvc-spring-mvc.ftl,mvc-spring-mybatis.ftl,mvcInterceptor.ftl三个主要模板,事务的相关配置在mvc-spring-mybatis.ftl当中,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 引入配置文件 -->
<context:annotation-config />
<context:component-scan base-package="${packageName}.*" />
<!-- 加载连接数据库的参数配置文件 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
<!-- <value>classpath:redis.properties</value> -->
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<!-- 阿里 druid 数据库连接池 -->
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<!-- 数据库基本信息配置 -->
<property name="driverClassName" value="<#noparse>${</#noparse>jdbc.driver}" />
<property name="url" value="<#noparse>${</#noparse>jdbc.url}" />
<property name="username" value="<#noparse>${</#noparse>jdbc.username}" />
<property name="password" value="<#noparse>${</#noparse>jdbc.password}" />
<property name="filters" value="<#noparse>${</#noparse>jdbc.filters}" />
<!-- 最大并发连接数 -->
<property name="maxActive" value="<#noparse>${</#noparse>jdbc.maxActive}" />
<!-- 初始化连接数量 -->
<property name="initialSize" value="<#noparse>${</#noparse>jdbc.initialSize}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="<#noparse>${</#noparse>jdbc.maxWait}" />
<!-- 最小空闲连接数 -->
<property name="minIdle" value="<#noparse>${</#noparse>jdbc.minIdle}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis"
value="<#noparse>${</#noparse>jdbc.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis"
value="<#noparse>${</#noparse>jdbc.minEvictableIdleTimeMillis}" />
<property name="validationQuery"
value="<#noparse>${</#noparse>jdbc.validationQuery}" />
<property name="testWhileIdle" value="<#noparse>${</#noparse>jdbc.testWhileIdle}" />
<property name="testOnBorrow" value="<#noparse>${</#noparse>jdbc.testOnBorrow}" />
<property name="testOnReturn" value="<#noparse>${</#noparse>jdbc.testOnReturn}" />
<property name="maxOpenPreparedStatements"
value="<#noparse>${</#noparse>jdbc.maxOpenPreparedStatements}" />
<!-- 超过时间限制是否回收 -->
<property name="removeAbandoned"
value="<#noparse>${</#noparse>jdbc.removeAbandoned}" />
<!-- 1800 秒,也就是 30 分钟 -->
<property name="removeAbandonedTimeout"
value="<#noparse>${</#noparse>jdbc.removeAbandonedTimeout}" />
<!-- 关闭 abanded 连接时输出错误日志 -->
<property name="logAbandoned" value="<#noparse>${</#noparse>jdbc.logAbandoned}" />
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations">
<array>
<value>classpath*:/${packageName}/sqlmapper/*.xml</value>
</array>
</property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="annotationClass"
value="org.springframework.stereotype.Repository" />
<property name="basePackage" value="${packageName}.dao" />
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"></property>
</bean>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="advice"
transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="upda*" propagation="REQUIRED" />
<tx:method name="select*" read-only="true"></tx:method>
<tx:method name="likeSelect*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 切入点 -->
<aop:config>
<aop:pointcut id="mypoint"
expression="execution(* ${packageName}.service.impl.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="advice" pointcut-ref="mypoint"></aop:advisor>
</aop:config>
</beans>
这是个非常经典的springmvc的配置文件,相信大家都非常熟悉。事务上首先配置事务管理器,然后配置事务切面规则,最后设置切入点表达式,跟springboot代码配置的原理是一样的,只是springmvc采用了xml的配置方式
logback日志支持
日志对于一个系统的重要性不言而喻,他对于我们监控系统的运行状态,排查bug等有着不可替代的作用。相信很多朋友现在还在使用log4j,是时候考虑logback了,据说他的性能比log4j优秀数十倍。配置采用xml的配置方式,跟log4j的配置方法大同小异,在springboot中默认原生支持logback,而在springmvc当中使用logback需要引入相关的jar包依赖,并在web,xml当中配置监听器方能使用,关于jar包依赖大家可以使用生成器随便生成一个ssm项目自行查看。
对于logback的支持添加了logback.ftl,webXml.ftl模板,logback.ftl代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds"
debug="false">
<contextName>logback</contextName>
<!--输出到控制台 -->
<appender name="console"
class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level>
</filter> -->
<encoder>
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<!--按天生成日志 -->
<appender name="logFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>
log/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
</FileNamePattern>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{yyyy-MM-dd HH:mm:ss} -%msg%n
</Pattern>
</layout>
</appender>
<!--错误日志统一输出到这里 -->
<appender name="error"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>
errorlog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>365</MaxHistory>
</rollingPolicy>
<encoder
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n</pattern>
</encoder>
<!--日志文件最大的大小 -->
<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
<!-- 所有error日志都在这里 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- mybatis打印sql语句 -->
<logger name="${packageName}.dao" level="DEBUG"></logger>
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="logFile" />
<appender-ref ref="error"/>
</root>
</configuration>
配置方法看一眼自然明了。
webXml.ftl代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>${projectName}</display-name>
<!-- Spring和mybatis的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mybatis.xml</param-value>
</context-param>
<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>classpath:logback.xml</param-value>
</context-param>
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
</listener>
<!-- 编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- druid连接池 启用 Web 监控统计功能 start -->
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>*.js ,*.gif ,*.jpg ,*.png ,*.css ,*.ico ,/druid/*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<!-- druid连接池 启用 Web 监控统计功能 end -->
<!-- Spring监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 防止Spring内存溢出监听器 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- Spring MVC servlet -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
可以看到配置了LogbackConfigListener来对logback.xml进行解析,从而按照相应的规则生成日志。在这里提一句,生成器的数据库连接池依然使用druid,前台的模板引擎依然使用thymeleaf,跟springboot保持一致,关于为何使用thymeleaf,主要是懒得改模板了,哈哈,毕竟前台不是强项,如果大家有好的前台样式模板欢迎推荐给我,目前只有一套bootstrap。
SSM框架支持
虽说现在springboot越来越火,但是ssm依然为很多个人和公司所用,他代表了一个时代。新版的代码生成器已经可以完美生成ssm项目,导入eclipse、idea发布到tomcat即可运行。但是有一个问题很奇怪,生成的ssm项目在运行的过程中有时会报java.lang.NoClassDefFoundError: org/springframework/web/context/request/async/CallableProcessingInte这个错误,但是clean一下tomcat重新发布项目即可正常运行,也就没再管它,欢迎知道的大佬告知!
运行效果
现在一起来看一下生成器的运行效果吧,以ssm项目为例,这里我就只截图了,不懂的朋友请翻阅之前的博客查看
生成代码的时候加入了一个友情提示,确认相关的信息
去eclipse/idea中导入生成的项目,发布到tomcat运行,建议使用tomcat8.5
本次更新到此结束,感谢观看!