在学习SSM框架后跃跃欲试想要亲手试试整合一个项目的过程,在这里分层次的介绍一下整个流程。
Github源码https://github.com/zzs2019/SSM.git
首先介绍下代码目录结构:
1、sql语句
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`money` double DEFAULT NULL,
PRIMARY KEY (`id`)
)
2、实体类domain
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
3、接口Dao类
public interface IAccountDao {
/*
查询所有账户
*/
@Select("select * from account")
List<Account> findAll();
/*
按id查询
*/
@Select("select * from account where id = #{id}")
Account findById(int id);
/*
保存账户
*/
@Insert("insert into account(name,money) values(#{name},#{money})")
void saveAccount(Account account);
/*
修改用户信息
*/
@Update("update account set name = #{name},money=#{money} where id = #{id}")
void updateAccount(Account account);
/*
删除用户信息
*/
@Delete("delete from account where id = #{id}")
void deleteAccount(int id);
}
4、服务层service的Dao类
public interface IAccountService {
/*
查询所有账户
*/
List<Account> findAll();
/*
按id查询
*/
Account findById(int id);
/*
保存账户
*/
void saveAccount(Account account);
/*
修改用户信息
*/
void updateAccount(Account account);
/*
删除用户信息
*/
void deleteAccount(int id);
/*
测试事物回滚
*/
void TestTransaction() ;
}
5、服务层service接口的实现类
@Service(value="accountService")
public class AccountServiceImp implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public List<Account> findAll() {
System.out.println("服务层执行《查询所有用户》。。。");
return accountDao.findAll();
}
@Override
public Account findById(int id) {
System.out.println("服务层执行《按ID查询用户》。。。");
return accountDao.findById(id);
}
@Override
public void saveAccount(Account account) {
System.out.println("服务层执行《保存用户》。。。");
accountDao.saveAccount(account);
throw new RuntimeException();
}
@Override
public void updateAccount(Account account) {
System.out.println("服务层执行《更新用户》。。。");
accountDao.updateAccount(account);
}
@Override
public void deleteAccount(int id) {
System.out.println("服务层执行《按ID删除用户》。。。");
accountDao.deleteAccount(id);
}
public void TestTransaction(){
Account a = new Account();
a.setName("aa");
a.setMoney(111);
Account b = new Account();
b.setName("bb");
b.setMoney(22);
accountDao.saveAccount(a);
int i = 1/0;
accountDao.saveAccount(b);
}
}
6、控制层
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
private IAccountService accountService;
@RequestMapping("findAll")
public ModelAndView testFindAll(){
System.out.println("控制层执行了");
List<Account> accounts = accountService.findAll();
for (Account account:accounts){
System.out.println(account);
}
ModelAndView mv = new ModelAndView();
mv.setViewName("findAll");
mv.addObject("list",accounts);
return mv;
}
@RequestMapping("findById")
public String testFindById(Model model,int id){
System.out.println("控制层执行了按ID查询");
Account ac = null;
ac = accountService.findById(id);
model.addAttribute("Account",ac);
return "findById";
}
@RequestMapping("saveAccount")
public String saveAccountTest(Account account){
accountService.saveAccount(account);
return "success";
}
@RequestMapping("updateAccount")
public String updateAccount(Account account){
accountService.updateAccount(account);
return "success";
}
@RequestMapping("deleteAccount")
public String deleteAccount(int id){
accountService.deleteAccount(id);
return "success";
}
@RequestMapping("/test")
public String Test(){
accountService.TestTransaction();
return "success";
}
}
7、web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--服务器一启动开始便加载springmvc配置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置过滤器解决中文乱码-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置spring监听器 项目启动的时候去加载spring 的配置文件 applicationContex.xml -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContex.xml</param-value>
</context-param>
</web-app>
8、springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描 只扫描控制层的-->
<context:component-scan base-package="com.zzs.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--开启注解支持-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--配置视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--放过静态资源-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
9、spring与mybatis整合的applicationContex.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 开启注解扫描(创建容器时要扫描的包) 忽略控制层的-->
<context:component-scan base-package="com.zzs">
<!--忽略控制层的controller注解扫描-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--*****************************************************************************************-->
<!--spring整合mybatis-->
<bean id="dataSource" class="com.mchange.v2.c3p0.DriverManagerDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm"/>
<property name="user" value="root"/>
<property name="password" value="zzs123456"/>
</bean>
<!--配置sqlsessionfactory工厂-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="com.zzs.domain"/>
</bean>
<!---配置AccountDao的位置-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zzs.DAO"></property>
</bean>
<!-- *****************************************************************************************-->
<!--spring框架声明事务管理-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!--事务AOP增强-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zzs.service.impl.*.*(..))"></aop:advisor>
</aop:config>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
至此SSM框架的整合完成,可以运行项目在前端进行测试,jsp等源代码已放在Github上https://github.com/zzs2019/SSM.git上。
在整合过程中遇到一个BUG,事务不能成功回滚。如果你的事务回滚没有成功且在网上试了各种异常类型、自调用等等可能引发的错误后还不能解决事务回滚,那么请听我下面的介绍。
在整合的时候发现事务没有回滚成功,然后再在测试类中测试可以成功回滚事务,然而在前端点击“测试事务回滚”时触发控制层的事务回滚测试却没有回滚事务,这是为什么呢?原来这是一个由于路径引发的血案。
在springmvc的配置文件中,扫描控制层时写的路径是com.zzs,这个包下面有dao类,有controller,有service,下意识以为expression里面的属性是自动扫描这个路径下的所有controller,有service注解什么的也没事,然而正是因为这个引发了血案。
在经过几天的排查后快要放弃时,看到了一篇博客,https://blog.youkuaiyun.com/p_doraemon/article/details/78912005,从中恍然大悟,在这个路径com.zzs后面加上控制层的路径变成com.zzs.controller再测试下回滚果然可以了。
然而在之前疯狂找bug疯狂搜博客的时候却没有发现这种问题,看来百度提问也是一门艺术,之前一直搜“为什么事务不能成功回滚”,然而换个问法“事务transanctional失效”,出来的确是不一样的解答。特此,写下这边博客纪念一下“程序猿寻找BUG之路”。