本章节主要是创建SSM项目。
SSM:代表着表现层SpringMVC、业务层Spring框架、持久层Mybatis三者的结合。需要使用Spring框架去整合SpringMVC和Mybatis。
1.创建简单的SpringMVC
2.在SpringMVC项目中创建Spring
3.Spring整合SpringMVC
4.SpringMVC项目中mybatis的单独运行
5.SpringMVC整合mybatis
6.SSM项目的mybatis事务
1.创建SpringMVC项目
1.1 IDEA创建SpringMVC项目
(1)创建SpringMVC项目
可以按照SpringMVC-01笔记 第 2.1节IDEA创建SpringMVC 中所记,创建SpringMVC项目。
(2)补全项目目录
(3).导入对应的项目依赖
我们创建的SSM项目需要导入的包:Spring的包、SpringMVC的包、SpringAOP的包、Spring事务的包、单元测试的包、mybatis的包、mysql的包。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<!--SpringAOP切面需要导入的包:spring-aop、aspectjweaver-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<!--Spring事务导入的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<!--SpringMVC导入的包:spring-webmvc、spring-web、servlet-api、jsp-api-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<!--单元测试junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<!--Spring整合mybatis的依赖包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
(4)在resources目录下创建springMVC.xml
SpringMVC项目启动之后会加载web.xml,然后加载SpringMVC.xml文件。
在SpringMVC.xml文件中,我们需要进行1.开启包扫描;2.开启SpringMVC对注解的支持;3.配置视图解析器。
此处需要非常注意的是,SpringMVC包扫描的路径应该是只包括controller层的,不应该包括service层和dao层。 否则到后面SpringMVC使用Spring整合mybatis事务的时候会出现事务不生效的情况。
<!--SpringMVC中通过mybatis进行事务控制出现事务无法生效:
1.SpringMVC.xml文件是mvc的配置文件,applicationContext.xml是Spring 的配置文件,事务控制在applicationContext.xml中配置
当SSM项目启动的时候,会先加载SpringMVC.xml,后加载applicationContext.xml,
如果在SpringMVC.xml加载的时候,将Service层的类扫描进去,后面加载applicationContext.xml就不会扫描Service层的类,事务也不会生效
为了让事务可以生效,在SpringMVC.xml中配置的包扫描路径应该是 : 不包含Service层,可以直接指定扫描controller层
注意:SpringMVC.xml必须不扫描service层,applicationContext.xml去扫描service层,事务才可以生效
-->
<context:component-scan base-package="com.Eheart.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--开启对springmvc注解的支持-->
<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>
(5)web.xml文件创建
SpringMVC项目启动的时候会加载web.xml文件,我们在web.xml中通过前端控制器去加载SpringMVC.xml文件。
<!--前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
(6)创建Controller层
@Controller
public class HelloController {
@Autowired
private IUserService userService;
@RequestMapping("/hello")
public String sayHello(){
System.out.println("hello");
return "success";
}
}
(7)index.jsp前端页面
<html>
<head>
<title>Index</title>
</head>
<body>
<!--相对路径访问hello,不需要添加前缀斜杠 / -->
<a href="hello">sayHello!!</a> <br/>
</body>
</html>
(8)将Springweb项目部署到tomcat服务器上
可以按照以下步骤将Springweb项目部署到tomcat服务器。
部署完项目之后,当启动tomcat服务器后,会弹出index.jsp的页面。我们点击 上面的定义的 sayHello!! 超链接,如果后台打印出 hello,且前端成功跳到success页面,则创建Springmvc成功。
1.2 在SpringMVC中创建Spring
此处我们创建的Spring是一个单独的Spring,还没有和SpringMVC整合的。
(1)创建对应的service、dao层、domain层文件和目录
在domain目录下,创建实体对象。
对service、dao下创建对应的接口、类文件并且添加注释:@Service、@Repository
- AccountService
package com.Eheart.service;
import com.Eheart.domain.Account;
import java.util.List;
public interface IAccountService {
public List<Account > findAllAccounts();
public void changeAccount(Account account1,Account account2,double money) ;
public Account findAccountById(Integer id);
}
- AccountServiceImpl
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao iAccountDao;
@Override
public List<Account> findAllAccounts() {
System.out.println("service层查询......findAllAccounts");
return iAccountDao.selectAllAccounts();
}
@Override
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void changeAccount(Account account1, Account account2, double money) {
System.out.println("service层......changeAccount");
account1.setMoney(account1.getMoney() - money);
account2.setMoney(account2.getMoney() +money);
iAccountDao.updateAccount(account1);
System.out.println("【"+account1+"】更新");
int i=1/0;
iAccountDao.updateAccount(account2);
System.out.println("【"+account2+"】更新");
}
@Override
public Account findAccountById(Integer id) {
System.out.println("service层.....findAccountById");
return iAccountDao.selAccountById(id);
}
}
- AccountDao
@Repository("accountDao")
public interface IAccountDao {
public Account selectAccountById(Integer id);
public void updateAccount(Account account);
}
- AccountDaoImpl
public class IAccountDaoImpl implements IAccountDao {
@Override
public Account selectAccountById(Integer id) {
System.out.println("dao层....selectAccountById");
return null;
}
@Override
public void updateAccount(Account account) {
System.out.println("dao层.....updateAccount");
}
}
(2)创建applicationContext.xml文件
在resources资源目录下,创建applicationContext.xml,这个是Spring的配置文件。
此处的applicationContext.xml,开启包扫描,应该扫描的路径是:service、dao层。controller层应该是springMVC进行扫描的包路径,但是spring扫描是后于springmvc扫描路径的,所以此处spring扫描的包路径包括controller也是可以的。
<?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">
<!--包扫描,不扫描controller层-->
<context:component-scan base-package="com.Eheart.*">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
</beans>
(3)测试Spring
通过ClassPathXmlApplicationContext 加载Spring的配置文件,我们可以通过bean的id获取对应的实体对象。
@Test
public void test01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService iUserService = ac.getBean("userService", IUserService.class);
iUserService.findAllUsers();
}
如果在这个测试案例中,可以通过iUserService.findAllUsers(); 调用service、dao层的方法,就说明Spring搭建好了。
1.3 Spring整合SpringMVC
在上面我们创建的Spring是单独的Spring,没有和SpringMVC整合的Spring。如果我们想通过SpringMVC的controller调用Spring的service、dao层,仅上面的操作是不可以的,我们还需要将Spring和SpringMVC整合到一起。
Spring整合SpringMVC的目的就是让我们可以通过controller调用service层,进而调用dao层操作数据库。
(1)修改web.xml
在web.xml中,我们配置 1.Spring的监听器listener、2、配置加载类路径的配置文件:applicationContext.xml。
通过web.xml的配置,可以让SpringMVC项目启动的时候,会加载applicationContext.xml的内容,可以让SpringMVC项目启动的时候整合Spring。
<!--在项目启动的时候,就去加载applicationContext.xml的配置文件,在web.xml中配置
ContextLoaderListener监听器(该监听器只能加载WEB-INF目录下的applicationContext.xml的配置文件)-->
<!-- 配置Spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置加载类路径的配置文件:applicationContext.xml -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
(2)修改controller层
controller层进行修改,通过service层的对象调用service层的方法。
@Controller
public class AccountController {
@Resource(name = "accountService")
private IAccountService iAccountService;
@RequestMapping("/changeAccount")
public String changeAccount(){
System.out.println("controller....changeAccount");
Account account1 = iAccountService.findAccountById(1);
System.out.println("查询到的账户1: 【"+account1+"】");
return "success";
}
}
(3)启动tomcat验证
启动tomcat,访问创建的changeAccount控制层,如果可以打印出service、dao层的信息,则说明Spring整合SpringMVC成功。
1.4在SpringMVC中mybatis单独运行
mybatis主要是通过 通过主配置文件加载1.数据库配置、2.mapper文件的配置。
当执行dao层的时候,可以通过dao层方法的全限定类名获取对应的mapper文件,根据方法名和mapper配置文件的id可以获取执行的sql语句。
(1)在resource目录下创建 mybatisConfig.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>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/dbtest"></property>
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</dataSource>
</environment>
</environments>
<!--配置mapper文件-->
<mappers>
<package name="com.Eheart.dao" ></package>
</mappers>
</configuration>
(2)定义dao层接口
@Repository("accountDao")
public interface IAccountDao {
@Select("select * from account where id = #{id} ")
public Account selectAccountById(Integer id);
@Update("update account set money = #{money} where id = #{id}")
public void updateAccount(Account account);
}
(3)测试mybatis
mybatis单独运行是通过SqlSession来进行的,而spring整合mybatis是通过SqlSessionFactoryBen。
public class MybatisTest {
@Test
public void test01(){
InputStream resource = MybatisTest.class.getClassLoader().getResourceAsStream("mybatisConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(resource);
SqlSession sqlSession = factory.openSession();
IUserDao iuserDao = sqlSession.getMapper(IUserDao.class);
List<User> users = iuserDao.selectAllUsers();
for (User user : users) {
System.out.println(user);
}
}
}
如果可以打印出查询的结果,则mybatis单独搭建成功
1.5 Spring整合mybatis
通过上面mybatis的单独搭建,我们知道mybatis生效需要数据源、mapper层接口和SqlSession。
而Spring整合mybatis,我们可以将数据源、mapper层定义到applicationContext.xml中,mybatis-spring提供了SqlSessionFactoryBean来代替SqlSession,我们需要在bean中定义SqlSessionFactoryBean.
(1)修改applicationContext.xml文件
Spring的配置文件 applicationContext.xml 除了之前包扫描之外,还需要添加数据源、SqlSessionFactoryBean的配置、mapper的配置。
<?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.Eheart.*">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://114.116.10.158:3306/dbtest"></property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="username" value="root"></property>
<property name="password" value="lyt@0925"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="datasource"></property>
<!-- 加载 mybatis 的全局配置文件 -->
<property name="configLocation" value="classpath:mybatisConfig.xml"></property>
</bean>
<!-- <!-- 配置 Mapper 扫描器 --> -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.Eheart.dao"/>
</bean>
</beans>
(2)启动tomcat测试
启动tomcat测试,访问controller层的方法,如果可以访dao层,并且进行数据库操作,说明springMVC整合mybatis成功了。
通过SpringMVC启动之后,访问controller层,我们可以通过Spring从IOC容器中获取到service、dao层的对象,进而dao层可以通过mybatis进行数据库操作,则说明SSM项目已经搭建完成了。
SSM中Spring、SpringMVC、Mybatis的不同的作用:
- SpringMVC的作用是表现层:启动之后可以有前端页面,通过前端页面访问controller层。
- Spring的作用主要是控制反转和依赖注入(切面AOP现在没有用到),将service、dao层的类对象创建在容器中,我们需要的时候从容器中取(即控制反转)。也可以给对象的变量赋值(即依赖注入)
- mybatis的主要作用在持久层,操作数据库。
1.6 SSM的mybatis事务
在SSM中通过mybatis操作数据库,如果开启mybatis的事务,需要进行以下操作。
(1)applicationContext.xml配置DataSourceTransactionManager和开启 spring 对注解事务的支持
applicationContext.xml除了包扫描、mybatis的数据池配置、mybatis的mapper配置、mybatis的SqlSessionFactoryBean配置,还需要配置:DataSourceTransactionManager和开启 spring 对注解事务的支持
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://114.116.10.158:3306/dbtest"></property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="username" value="root"></property>
<property name="password" value="lyt@0925"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="datasource"></property>
<!-- 加载 mybatis 的全局配置文件
<property name="configLocation" value="classpath:mybatisConfig.xml"></property>
-->
</bean>
<!-- 开启 spring 对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 配置扫描dao的包 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.Eheart.dao"/>
</bean>
(2)service层配置
以下通过直接进行service层的配置。
在Service类、方法上添加注释@Transactional
-
类上添加:@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
-
特定方法上添加:@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
这样的话,特定方法的运行就是原子性,遇到异常是会回滚的。 -
@Transactional的属性: readOnly 代表只读,为true生效。一般查询方法设置readOnly 为true,更新方法设置readOnly 为false。
- @Transactional的属性: propagation (事务的传播行为),常见的值有: REQUIRED、 SUPPORTS。
- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值),更新方法一般会设置:REQUIRED
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务),查询方法可以设置为SUPPORTS
@Service("accountService")
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
@Resource(name ="accountDao" )
private IAccountDao iAccountDao;
@Override
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void changeAccount(Account account1, Account account2, double money) {
account1.setMoney(account1.getMoney() - money );
account2.setMoney(account2.getMoney() + money );
iAccountDao.updateAccount(account1);
System.out.println("更新【"+account1+"】");
int i=1/0; //异常
iAccountDao.updateAccount(account2);
System.out.println("更新【"+account2+"】");
}
}
(3)调用service层方法:changeAccount()进行测试
在方法changeAccount()中,我们进行更新account、account2,
但是更新完account1之后会执行 int i=1/0; //异常
会出现异常,不会更新account2。如果事务生效的话,account1的更新是会回滚的,我们查询数据库就可以看到account1的更新没生效。
如果事务没有生效,出现的情况是account1更新了,但是account2没有更新。出现的场景就是:账户1扣了钱,但是没有将钱转给账户2。
(4)注意事项:SSM的mybatis事务不生效
● 检查上面的事务配置是否有错误;
● 检查SpringMVC.xml的包扫描的路径是否包括:Service层,SpringMVC.xml文件包扫描的路径应该不包含Service层。
1.SpringMVC.xml文件是mvc的配置文件,applicationContext.xml是Spring 的配置文件,事务控制在applicationContext.xml中配置。当SSM项目启动的时候,会先加载SpringMVC.xml,后加载applicationContext.xml,
如果在SpringMVC.xml加载的时候,将Service层的类扫描进去,后面加载applicationContext.xml就不会扫描Service层的类,事务也不会生效。为了让事务可以生效,在SpringMVC.xml中配置的包扫描路径应该是 : 不包含Service层,可以直接指定扫描controller层
注意:SpringMVC.xml必须不扫描service层,applicationContext.xml去扫描service层,事务才可以生效