Nutz项目整合Spring实战

前言

本教程主要基于本人Nutz框架的 Demo进行改造,目的是使原有Nutz demo代码可以正常运行,新增spring代码也可以正常运行,并且Nutz框架原有代码与spring新增代码可以相互引用,从而达到整合后的新代码与旧代码可以兼容,请配合项目源码进行操作。

Demo功能

登陆模块

查看用户列表

新增用户

通过自定义sql自动导出报表

Github地址

https://github.com/Evan43789596/evanshare.git

背景

某公司原有项目采用的是Nutz框架,无奈是Nutz框架整合其他主流框架会相对繁琐,加上Nutz对事务支持也不是很友好,而且也不便于团队成员使用和学习市面主流技术,于是我们想把Nutz与spring框架整合,在不影响原有项目的前提下,新代码均使用spring实现,也就意味着把nutz的东西都转移到spring去管理了。

目标

  1. 原有nutz的业务代码不需要改动能正常运行
  2. 新做的spring的代码可以正常运行
  3. nutz可以引用spring的代码
  4. spring可以引用nutz的代码

方案

  1. 保留两套MVC,Spring MVC与Nutz MVC共存,原有旧代码继续走Nutz MVC
  2. Nutz与Spring统一使用Spring IOC容器进行管理,均从Spring上下文查找bean
  3. 原有Nutz配置搬到Spring ,由Spring统一加载与管理,AOP与事务均使用Spring实现

思路

  1. Nutz Mvc 与 Spring Mvc相互独立存在,项目原有代码继续使用Nutz Mvc,新建Spring代码采用Spring Mvc,根据请求路径中的前缀/spring/进行切换。
  2. 原有Nutz项目的bean与新做的spring相关bean统一交给springIOC容器管理,覆盖NutzIOC注解(IocBean、Inject、InjectName)使这些注解标注的类实例交由Spring管理
  3. 创建SpringIocProvider实现Ioc、IocProvider,使原有Nutz代码与新代码上下文统一使用applicationContext
  4. 通过Spring AutowiredAnnotationBeanPostProcessor类中的setAutowiredAnnotationTypes方法,可使开发者扩展自定义注解交给spring去自动注入(如:Inject),从而实现spring的代码也能使用Inject注解

    把原有nutz项目的所有配置统一交由spring去加载,这样后面的事情都按照spring的方式去做即可。

文件变更

  • 在com.evanshare包下,新增spring相关Controller与service,其功能与原Nutz部分相同
  • 新增SpringIocProvider
  • 修改MainModule原有IocBy的类型,改为SpringIocProvider
  • 新增org.nutz包,用于复写Nutz Ioc相关注解
  • 修改ResourceFilter,新增对/spring/过滤
  • 修改web.xml,加入spring相关配置
  • 在resource目录下新增application.xml、SpringMVC-servlet.xml以及其他相关.xml,把原有Nutz配置搬到 .xml
  • 对com.evanshare.module原有nutz部分module引入spring相关service与注解

实现步骤

1.加入springMvc与Spring 相关配置

打开WEB-INF下的web.xl,在原有配置基础上加入如下配置

<!--引入spring-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
<!-- 默认是/WEB-INF/applicationContext.xml -->
</context-param> <listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener> <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*:SpringMVC-servlet.xml</param-value>
<!-- 默认是/WEB-INF/[servlet名字]-servlet.xml -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>

2.新增Spring相关配置

在resource目录下,创建applicationContext.xml,加入如下配置

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <import resource="conf/*.xml" /> </beans>

在resource目录下,创建SpringMVC-servlet.xml,加入如下配置

<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 设置使用注解的类所在的jar包 -->
<context:component-scan base-package="com.evanshare" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix">
<value>/WEB-INF/jsp/</value>
</property> <property name="suffix">
<value>.jsp</value>
</property> </bean> </beans>

在Resource下,分别创建jdbc.xml、nutDao.xml、property.xml、transation.xml、annotation.xml

jdbc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- druid-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}"/>
<property name="testWhileIdle" value="true"/>
<property name="validationQuery" value="${db.validationQuery}"/>
<property name="maxActive" value="${db.maxActive}"/>
<property name="connectionProperties" value="druid.stat.slowSqlMillis=2000"/>
</bean> </beans>

nutzDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="dao" class="org.nutz.dao.impl.NutDao">
<property name="dataSource" ref="dataSource"/>
<!-- 如果要使用Trans,移除springDaoRunner -->
<property name="runner" ref="springDaoRunner"/>
</bean>
<bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
</bean> </beans>

property.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 读取jdbc配置 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- jdbc配置 -->
<value>classpath:properties/db.properties</value>
</list>
</property>
</bean>
</beans>

transation.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="dao" class="org.nutz.dao.impl.NutDao">
<property name="dataSource" ref="dataSource"/>
<!-- 如果要使用Trans,移除springDaoRunner -->
<property name="runner" ref="springDaoRunner"/>
</bean>
<bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
</bean> </beans>

annotation.xml

<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--spring 扫包 @Service .....-->
<context:component-scan base-package="com.evanshare">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <context:annotation-config/> <!-- 添加自定义注解,使spring自动注入-->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
<property name="autowiredAnnotationTypes" >
<set >
<value >org.nutz.ioc.loader.annotation.Inject</value>
</set>
</property>
</bean>
</beans>

注意:

以下annotation.xml中的这段配置,是为了让spring能识别我们的自定义注解,并且能为我们用自定义注解(Nutz的@Inject)标识的对象注入,我们如果想加多几个自定义注解,只需要像一下配置那样把全限定类名加入到属性中即可

 <!-- 添加自定义注解,使spring自动注入-->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
<property name="autowiredAnnotationTypes" >
<set >
<value >org.nutz.ioc.loader.annotation.Inject</value>
</set>
</property>
</bean>

Tips

为什么我会知道?参考Spring AutowiredAnnotationBeanPostProcessor源码注释可以看到,其实它本身就是支持开发者自由扩展的

    /**
* Set the 'autowired' annotation types, to be used on constructors, fields,
* setter methods and arbitrary config methods.
* <p>The default autowired annotation type is the Spring-provided
* {@link Autowired} annotation, as well as {@link Value}.
* <p>This setter property exists so that developers can provide their own
* (non-Spring-specific) annotation types to indicate that a member is
* supposed to be autowired.
*/
public void setAutowiredAnnotationTypes(Set<Class<? extends Annotation>> autowiredAnnotationTypes) {
Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
this.autowiredAnnotationTypes.clear();
this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes);
}

3.新增SpringIocProvider,

具体SpringIocProvider代码请查看项目源码,主要看下面这段代码:

public class SpringIocProvider implements IocProvider, Ioc {

    protected  ApplicationContext applicationContext;

    @Override
public Ioc create(NutConfig config, String[] args) {
if (config == null || Lang.length(args) > 0)
applicationContext = new ClassPathXmlApplicationContext(args);
else
applicationContext = (ApplicationContext) config.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
return this;
} @SuppressWarnings("unchecked")
@Override
public <T> T get(Class<T> classZ) throws IocException {
InjectName injectName = classZ.getAnnotation(InjectName.class);
if (injectName != null && !Strings.isBlank(injectName.value()))
return (T) applicationContext.getBean(injectName.value());
return (T) applicationContext.getBean(applicationContext.getBeanNamesForType(classZ)[0]);
}
}

从以上代码可以看出,该create和get方法会返回一个applicationContext,也就是说,Nutz Ioc如果是使用该iocProvider去创建,那么Nutz 在查找bean的时候将会从spring的上下文中去查找了,从而达到了我们想要的spring管理bean的效果,可对比Nutz原生ComboIocProvider.

在主模块声明SpringIocProvider

在MainModule上加入@IocBy(type=SpringIocProvider.class,args={})

@ChainBy(args="conf/mvc/evanshare-mvc-chain.js")
@SetupBy(value=MainSetup.class)
//@IocBy(type=CustomComboIocProvider.class,args={"*js","conf/ioc/","*anno","com.evanshare","*tx"})
@IocBy(args = {} ,type = SpringIocProvider.class)
@Modules(scanPackage=true)
@Fail("jsp:jsp.500")
@Localization(value="msg/", defaultLocalizationKey="zh-CN")
public class MainModule {
}

意味着nutz的所有bean都将会从spring上下文中查找

4.重写Nutz IOC注解

为了使项目中原有nutz IOC注解标识的类能交给spring容器管理,我们需要做一些小的改动,就是在原注解上加入spring的注解@Component,这样的话,spring就会帮我们将这些类纳入到spring容器管理.需要重写的注解有:@Inject、@IocBean、@InjectName,以下取IocBean作为示例,其他同理。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface IocBean {

5.新增spring相关controller与service

在com.evanshare包下,新建Spring相关Controller: SpUserController、SpSqlController,新建Spring相关Service:SpUserService、SpSqlService

作用

SpUserController:基于spring实现,用于用户操作相关模块,作用与Nutz demo中的UserModule相同,用于用户登录、添加用户、用户列表展示

SpSqlController:基于Spring实现,用于Sql查询相关模块,作用与Nutz demo中的SqlController中的SqlModule相同,用于根据sql导出报表

SpUserService:基于Spring实现,用于用户相关处理逻辑,

与Nutz demo中的UserService作用相同,可互相替换使用

SpSqlService:基于Spring实现,用于sql相关处理逻辑,

与Nutz demo中的SqlService作用相同,可互相替换使用

SpUserController

/**
*
* 基于spring实现的Sql Controller
* Created by liangyh on 2017/1/24.
* Email:10856214@163.com
*/
@Controller
public class SpSqlController {
@Autowired
private SpSqlService spSqlService;
//注入nutz service
@Inject
private SqlService sqlService; /**
* 跳转到sql查询页面
* @return
*/
@RequestMapping(value = "/sql/sql_query")
public String forwardToSqlQuery(){ return "sql/sql_query"; } /**
* 根据自定义sql到处报表
* @param querySql
* @param resp
* @throws Exception
*/
@RequestMapping("/sql/exportData")
public void exportSqlData(@RequestParam(value = "querySql",required = false) String querySql, HttpServletResponse resp) throws Exception {
spSqlService.exportExcelBySql(querySql,resp);
//调用Nutz service去到处报表
// sqlService.exportExcelBySql(querySql,resp);
}

SpUserController

/**
* 基于spring实现的用户controller
* Created by liangyh on 2017/1/28.
* Email:10856214@163.com
*/
@RestController
public class SpUserController {
@Autowired
private SpUserService spUserService;
//注入nutz的service
@Inject
private UserService userService; /**
* 用户登录
*
* @param name
* @param password
* @param session
* @return
*/
@RequestMapping("/user/login")
public String login(@RequestParam("username") String name, @RequestParam("password") String password, HttpSession session) {
User user = spUserService.findByNameAndPsw(name,password);
if (user == null) {
return "/page/error/500";
} else {
session.setAttribute("user", user);
return "jsp/common/index_menu";
}
} /**
* 获取用户列表
*
* @param session
* @return
*/
@RequestMapping("/user/list")
public String list(@RequestParam(value = "pageNum",required = false) String pageNum,@RequestParam(value = "numPerPage",required = false) String numPerPage ,@RequestParam(value = "keyword",required = false) String keyword,HttpSession session,HttpServletRequest req) {
Integer page = StringUtils.isNotBlank(pageNum)?Integer.parseInt(pageNum):0;
Integer perPageNum = StringUtils.isNotBlank(numPerPage)?Integer.parseInt(numPerPage):0;
//调用Nutz service获取用户列表
// QueryResult result = userService.getUserList(page, perPageNum,keyword);
QueryResult result = spUserService.getUserList(page, perPageNum,keyword);
List<User> users = result.getList(User.class);
PagerModel pager = new PagerModel();
pager.setCurrentPage(page);
pager.setNumPerPage(perPageNum);
pager.setPageNumShown(result.getPager().getPageSize());
pager.setTotalCount(result.getPager().getRecordCount());
pager.setPageSize(result.getPager().getPageSize());
req.setAttribute("users", users);
req.setAttribute("pager", pager);
return "user/user_list";
}

SpUserService

/**
* 基于spring 的service
* Email:10856214@163.com
*/
@Service
public class SpUserService {
@Autowired
private Dao dao; /**
* 获取用户列表
* @param pageNumber
* @param pageSize
* @param keyword
* @return
*/
public QueryResult getUserList(int pageNumber,int pageSize,String keyword){
if(StringUtils.isEmpty(keyword)){
keyword = "";
}
Pager pager = dao.createPager(pageNumber, pageSize);
List<User> userList =dao.query(User.class, Cnd.where("name","like","%"+keyword+"%"), pager);
return new QueryResult(userList,pager);
} /**
* 根据姓名和密码查找用户
* @param name
* @param password
* @return
*/
public User findByNameAndPsw(String name,String password){
return dao.fetch(User.class, Cnd.where("name", "=", name).and("password", "=", password));
} public void print(){
System.out.println("I AM NUTZ SERVICE");
} /**
* 获取用户总数
* @return
*/
public Integer getTotalUserCount(){ return dao.count(User.class);
} /**
* 添加用户
* @param user
* @return
*/
public User addUser(User user) {
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
return dao.insert(user); } }

SpSqlService

/**
* Created by liangyh on 2017/1/28.
* Email:10856214@163.com
*/
@Service
public class SpSqlService { private final Logger logger = Logger.getLogger(SqlModule.class); @Autowired
private Dao dao; /**
* 根据自定义sql导出报表
* @param insql 自定义sql
* @param resp 响应实体
* @throws SQLException
* @throws IOException
* @throws FileNotFoundException
* @throws JSQLParserException
*/
public void exportExcelBySql(String insql,HttpServletResponse resp)
throws Exception {
// 11:创建一个excel
SXSSFWorkbook book = new SXSSFWorkbook(1000);
java.sql.ResultSet rs = null;
List<String> tables = getTables(insql);
DataSource datasource = Mvcs.getIoc().get(DruidDataSource.class, "dataSource");
Connection conn = null;
Statement st =null;
try {
conn = datasource.getConnection();
// 3:声明st
double maxRowNum;
int sheets;
ResultSetMetaData rsmd;
int cols;
long startTime;
long endTime;
st = conn.createStatement(); //结果集总行数
double totalcount = 137713; /* excel单表最大行数是65535 */
maxRowNum = 60000; sheets = (int) Math.ceil(totalcount / maxRowNum);
rs = st.executeQuery(insql);
// 3:rsmd
rsmd = (ResultSetMetaData) rs.getMetaData();
// 4:分析一共多少列
cols = rsmd.getColumnCount();
startTime = System.currentTimeMillis(); //写入excel文件数据信息
for (int i = 0; i < sheets; i++) {
logger.info("<=======正在导出报表=========>"); // 12:创建sheet
SXSSFSheet sheet = book.createSheet(" " + (i + 1) + " "); // 13:创建表头
SXSSFRow row = sheet.createRow(0); for (int j = 0; j < cols; j++) {
String cname = rsmd.getColumnName(j + 1); SXSSFCell cell = row.createCell(j);
cell.setCellValue(cname); }
// 显示数据
for (int t = 0; t < maxRowNum; t++) {
if (!rs.next()) {
break;
}
// 14:保存数据
row = sheet.createRow(sheet.getLastRowNum() + 1); for (int j = 0; j < cols; j++) {
SXSSFCell cell = row.createCell(j);
cell.setCellValue(rs.getString(j + 1));
}
}
} resp.reset();
resp.setContentType("multipart/form-data"); //自动识别
resp.setHeader("Content-Disposition", "attachment;filename=data.xls");
book.write(resp.getOutputStream());
endTime = System.currentTimeMillis();
logger.info("导出报表所用时间为:" + (endTime - startTime));
}catch (Exception ex){
logger.info(String.format("导出报表异常,Sql=【%s】,异常信息{}",insql),ex); }finally {
if(book!=null){
book.close();
}
if(rs!=null){
rs.close();
}
if(st!=null){
st.close();
}
if(conn!=null){
conn.close();
}
}
} /**
* 获取对应的表
* @param insql 自定义sql
* @return
* @throws JSQLParserException
*/
private List<String> getTables(String insql) throws JSQLParserException{
net.sf.jsqlparser.statement.Statement statement = CCJSqlParserUtil.parse(insql);
Select selectStatement = (Select)statement;
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> result = tablesNamesFinder.getTableList(selectStatement);
return result;
}

猜想

基于Spring实现的SpUserService与基于Nutz实现的UserService相互替换使用,Spring实现的Controller既然调用spring的service,也能调用nutz的service(SpSqlService与SqlService同理)

6.事务托管给Spring

在org.nutz下创建SpringDaoRunner,SpringDaoRunner继承NutDaoRunner,重写了_run方法,通过DataSourceUtils去获取connection,使得事务可以统一交给Spring去管理

public class SpringDaoRunner extends NutDaoRunner {

    @Override
public void _run(DataSource dataSource, ConnCallback callback) { Connection con = DataSourceUtils.getConnection(dataSource);
try {
callback.invoke(con);
} catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new RuntimeException(e);
} finally {
DataSourceUtils.releaseConnection(con, dataSource);
}
}
}

回顾之前的nutDao.xml配置,该配置作用是使用spring管理事务

<bean id="dao" class="org.nutz.dao.impl.NutDao">
<property name="dataSource" ref="dataSource"/>
<!-- 如果要使用Trans,移除springDaoRunner -->
<property name="runner" ref="springDaoRunner"/>
</bean>
<bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
</bean>

7.修改ResourceFilter

在ResourceFilter中的init方法中,新增对请求路径中/spring/过滤

/**
* 对资源文件进行过滤
* @author lyh
*
*/
public class ResourceFilter extends NutFilter {
protected Set<String> prefixs = new HashSet<String>();
@Override
public void init(FilterConfig conf) throws ServletException {
super.init(conf);
prefixs.add(conf.getServletContext().getContextPath() + "/druid/");
//对请求路径带有/spring/过滤
prefixs.add(conf.getServletContext().getContextPath() + "/spring/");
} @Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
if (req instanceof HttpServletRequest) {
String uri = ((HttpServletRequest) req).getRequestURI();
for (String prefix : prefixs) {
if (uri.startsWith(prefix)) {
chain.doFilter(req, resp);
return;
}
}
}
super.doFilter(req, resp, chain);
} }

为什么要过滤?

为了应对基于spring controller请求可以走Spring Mvc,所以在ResourceFilter那进行了过滤,避免被NutFilter进行拦截和调用到Nutz MVC的处理方法,这样请求可以顺利进入DispatcherServlet

8.新增基于Spring代码实现的菜单

在原Nutz demo菜单基础上,新增两个指向Spring处理类的菜单,只想Spring处理类的Url前缀都加上/spring/,用于区分请求是走Spring MVC还是Nutz Mvc

    <ul class="tree treeFolder">
<li><a href="<%=request.getContextPath() %>/user/list" target="navTab" rel="user_list">用户列表</a></li>
</ul>
</li> </ul>
<ul class="tree treeFolder">
<li><a href="<%=request.getContextPath() %>/sql/sql_query" target="navTab" rel="sql_query">SQL查询</a></li>
</ul>
</li> </ul>
<ul class="tree treeFolder">
<li><a href="<%=request.getContextPath() %>/spring/user/list" target="navTab" rel="spring_user_list">基于Srping实现用户列表</a></li>
</ul>
</li> </ul>
<ul class="tree treeFolder">
<li><a href="<%=request.getContextPath() %>/spring/sql/sql_query" target="navTab" rel="springsql_query">基于Spring实现SQL查询</a></li>
</ul>
</li> </ul>

菜单显示如下

测试

1.改造前后兼容测试

测试案例

  1. 浏览器输入:localhost:8080/evanshare,跳转到登陆页面,输入:admin 123456
  2. 点击资管管理-用户列表
  3. 点击资管管理-Sql查询
  4. 点击资管管理-基于Springle实现用户列表
  5. 点击资管管理-基于Springle实现Sql查询

预期结果

  1. 能正常登陆,进入管理后台
  2. 能正常打开用户列表
  3. 输入select * from tb_at_user ,能正常导出报表
  4. 能正常打开用户列表
  5. 输入select * from tb_at_user ,能正常导出报表

2.事务测试

新建NutzTransTestService

在com.evanshare.spring.service包下,新建NutzTransTestService

package com.evanshare.spring.service;

import org.nutz.dao.Dao;
import org.springframework.transaction.annotation.Transactional; /**
* 该类用于事务测试用途
* Created by liangyh on 2017/1/28.
* Email:10856214@163.com
*/
public class NutzTransTestService { protected Dao dao; public void setDao(Dao dao) {
this.dao = dao;
} @Transactional(rollbackFor=Exception.class)
public void doUserClear() {
dao.clear("tb_at_user");
throw new RuntimeException();
} }

在Test目录下com.evanshare.spring.service包新建NutzTransTestServiceTest

package com.evanshare.spring.service;

import org.junit.Test;
import org.nutz.dao.Dao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.Assert; import static org.junit.Assert.*; /**
* NUtz事务测试
* Created by liangyh on 2017/1/28.
* Email:10856214@163.com
*/
public class NutzTransTestServiceTest extends Assert { @Test
public void doUserClear() throws Exception {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
NutzTransTestService nutzTest = ctx.getBean("transTest", NutzTransTestService.class); Dao dao = ctx.getBean("dao", Dao.class);
int count = dao.count("tb_at_user");
assertTrue(count > 0);
try {
nutzTest.doUserClear();
} catch (Exception e) {
// 里面主动抛出异常
}
assertEquals(count, dao.count("tb_at_user"));
ctx.close();
} }

测试案例

  1. 执行NutzTransTestServiceTest下的doUserClear方法

预期结果

  • NutzTransTestService有一个spring事务注解的方法,里面清除了t_user表,然后抛出了异常
  • 日志中显示, spring的事务进行了rollback, 可以看到rollback等字眼
  • 执行doUserClear前后的tb_at_user表记录数相同,所以事务真的是回滚了

执行日志如下:

22:17:31.483 [main] DEBUG org.nutz.dao.impl.sql.run.NutDaoExecutor - DELETE FROM tb_at_user
22:17:31.503 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback
22:17:31.503 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@2228db21]
22:17:31.511 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2228db21] after transaction
22:17:31.511 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
22:17:35.556 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
22:17:35.556 [main] DEBUG org.nutz.dao.impl.sql.run.NutDaoExecutor - SELECT COUNT(*) FROM tb_at_user
22:17:35.558 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource

最新文章

  1. JVM之SerialOld收集器
  2. CentOS(RedHat)命令行永久修改IP地址、网关、DNS
  3. Angular Input格式化
  4. 【JavaEE企业应用实战学习记录】logFilter
  5. Linux下Redis安装与PHP扩展(PHP7适用)
  6. 【温故而知新-Javascript】使用 Window 对象
  7. EXCEL插件
  8. win7 下配置 java 环境变量
  9. 利用Console来调试JS程序、Console用法总结
  10. C#共享内存实例 附源码
  11. Tomcat与web程序结构与Http协议
  12. ASP.NET中的Excel操作(NPOI方式)
  13. 最接近原生APP体验的高性能前端框架——MUI
  14. 读书笔记-《Maven实战》-2018/4/18
  15. markdown小知识总结
  16. 运行pip报错:Fatal error in launcher: Unable to create process using &#39;&quot;&#39;
  17. eMMC分区详解【转】
  18. slack机器人运维
  19. JZ2440 裸机驱动 第7章 内存管理单元MMU
  20. 配置 tsconfig.json

热门文章

  1. mysql中information_schema.triggers字段说明
  2. JIRA从8.1.0升级到8.3.0
  3. Java并发编程系列-(9) JDK 8/9/10中的并发
  4. 洛谷$P1155$ 双栈排序 贪心+二分图匹配
  5. yum回滚至Kubernetes1.15.7版本
  6. 单机Web后端接口服务压力测试
  7. zTree 节点勾选取消勾选 选中取消选中
  8. cannot open git-upload-pack,cannot open git-receive-pack,Can&#39;t connect to any URI错误解决方法eclipse
  9. hadoop配置环境变量
  10. es lucene搜索及聚合流程源码分析