1. 课程介绍
- 1. SSJ集成;(掌握)
- 2. 声明式事务管理;(掌握)
- 什么是三大框架
2.1. ssh
Struts/Struts2 Spring Hibernate
2.2. ssm
SpringMVC Spring MyBatis
2.3. ssj
Struts2 Spring JPA
SpringMVC Spring JPA
SpringMVC Spring Data JPA
- Spring集成JPA;(掌握)
Spring4 + Struts2 + jpa/hibernate4
建议:先完成Spring与jpa的集成
3.1. 创建动态web工程:
注意修改classpath路径,创建web文件取名为webapp
3.2. 拷贝Spring共16的jar文件
11个spring的jar文件
spring-framework-3.2.0.RELEASE-dependencies搜索dbcp,pool,aop,wea,logging
3.3. 拷贝JPA共11的jar文件
hibernate-release-4.3.8.Finallibjpa*.jar
hibernate-release-4.3.8.Finallib equired*.jar
3.4. 拷贝数据库驱动1个jar文件
Spring+jpa+驱动共28个jar文件
3.5. 写一个domain对象,配置JPA映射
@Entity
@Table(name = "t_product")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
3.6. Spring的配置文件
3.6.1. Bean对象注入的顺序
jdbc.properties->dataSource->entityManagerFactory->dao->service->junit->action
3.6.2. 加载jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///ssj
jdbc.username=root
jdbc.password=root
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载jdbc.properties -->
<context:property-placeholder location="jdbc.properties" />
3.6.3. 配置连接池dataSource
<!-- 配置连接池dataSource -->
<!-- destroy-method="close当前bean销毁的时候,会先调用close方法,关闭连接" -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- 依赖注入连接池需要的属性 -->
<!-- property name="是BasicDataSource的set方法,本质属性" -->
<!-- property value="是jdbc.properties配置文件的key" -->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
3.7. 配置entityManagerFactory
3.7.1. Spring-Reference_2.5_zh_CN.chm
12.6.1.3. LocalContainerEntityManagerFactoryBean
3.7.2. 配置信息
<!-- org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter引入默认entityManagerFactory名称 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 1.注入DataSource -->
<property name="dataSource" ref="dataSource" />
<!-- 2.从哪个包去扫描@Entity,domain包 -->
<!-- public void setPackagesToScan(String... packagesToScan) { -->
<property name="packagesToScan" value="cn.itsource.ssj.domain" />
<!-- 3.配置JPA的实现 -->
<!-- private JpaVendorAdapter jpaVendorAdapter; -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter -->
<!-- private boolean showSql = false;是否显示sql语句 -->
<property name="showSql" value="true" />
<!-- private boolean generateDdl = false;是否建表 -->
<property name="generateDdl" value="true" />
<!-- private String databasePlatform;原来方言 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
</property>
</bean>
3.8. IProductDao
public interface IProductDao {
void save(Product product);
void update(Product product);
void delete(Long id);
Product get(Long id);
List<Product> getAll();
}
3.9. ProductDaoImpl
@Repository
public class ProductDaoImpl implements IProductDao {
// @Autowired 不能使用这个注入
@PersistenceContext // 持久层上下文管理器
private EntityManager entityManager;
@Override
public void save(Product product) {
entityManager.persist(product);
}
@Override
public void update(Product product) {
entityManager.merge(product);
}
@Override
public void delete(Long id) {
Product product = get(id);
if (product != null) {
entityManager.remove(product);
}
}
@Override
public Product get(Long id) {
return entityManager.find(Product.class, id);
}
@Override
public List<Product> getAll() {
String jpql = "select o from Product o";
return entityManager.createQuery(jpql).getResultList();
}
}
3.10. 组件扫描
<!-- 扫描dao、service、action组件 -->
<!-- 可以处理@Repository, @Service, and @Controller,@Autowired,@PersistenceContext 注解-->
<context:component-scan base-package="cn.itsource.ssj" />
3.11. IProductService
public interface IProductService {
void save(Product product);
void update(Product product);
void delete(Long id);
Product get(Long id);
List<Product> getAll();
}
3.12. ProductServiceImpl
@Service
public class ProductServiceImpl implements IProductService {
@Autowired
private IProductDao productDao;
@Override
public void save(Product product) {
productDao.save(product);
}
@Override
public void update(Product product) {
productDao.update(product);
}
@Override
public void delete(Long id) {
productDao.delete(id);
}
@Override
public Product get(Long id) {
return productDao.get(id);
}
@Override
public List<Product> getAll() {
return productDao.getAll();
}
}
3.13. 声明式事务管理(注解版本)
在spring的配置文件添加一点事务配置,并且在service层类上面添加一些注解,就可以实现事务管理
3.13.1. 添加一个tx命名空间
<?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: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/tx
3.13.2. 添加事务配置
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 开启注解事务管理 ,解析@Transactional事务注解 -->
<!-- transaction-manager="transactionManager"默认找bean.id=transactionManager事务管理器 -->
<tx:annotation-driven />
3.13.3. ProductServiceImpl
@Service
// 默认事务配置
// @Transactional
// 上面配置等价于下面配置
@Transactional(propagation = Propagation.REQUIRED)
public class ProductServiceImpl implements IProductService {
@Autowired
private IProductDao productDao;
@Override
public void save(Product product) {
productDao.save(product);
}
@Override
public void update(Product product) {
productDao.update(product);
}
@Override
public void delete(Long id) {
productDao.delete(id);
}
@Override
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public Product get(Long id) {
return productDao.get(id);
}
@Override
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public List<Product> getAll() {
return productDao.getAll();
}
}
3.14. Junit
@Autowired
IProductService productService;
@Test
public void save() throws Exception {
System.out.println("代理类:" + productService.getClass());
Product product = new Product();
product.setName("苹果的弟弟");
productService.save(product);
}
代理类:class com.sun.proxy.$Proxy23
- Spring集成Struts2
4.1. 添加struts2的12个jar文件
没有导入javassist-3.11.0.GA.jar
4.2. 配置web.xml
<!-- 添加struts核心过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.3. 配置struts.xml
<package name="default" namespace="/" extends="struts-default">
<!-- action代表一个控制器, name="对应的访问的url地址" method="返回action的方法" -->
<!-- spring集成struts之后,class可以写全限定类名,也可以写bean.id -->
<!-- product 没有写_的时候,默认就是访问当前action的execute -->
<!-- product_*写了_input,访问input方法 ,对应后面method={1} -->
<action name="product_*" class="cn.itsource.ssj.action.ProductAction" method="{1}">
<!-- 显示列表页面 -->
<result>/WEB-INF/views/product.jsp</result>
<!-- 显示添加获取修改页面 -->
<result name="input">/WEB-INF/views/product_input.jsp</result>
<!-- 保存或者删除之后进行重定向 -->
<result name="reload" type="redirectAction">product</result>
</action>
</package>
4.4. ProductAction
public class ProductAction extends ActionSupport {
@Autowired
IProductService productService;
@Override
public String execute() throws Exception {
ActionContext.getContext().put("products", productService.getAll());
return SUCCESS;
}
}
4.5. 配置好,启动tomcat,抛出一下异常
4.5.1. 具体异常信息
严重: Exception starting filter struts2
Class: com.opensymphony.xwork2.spring.SpringObjectFactory
File: SpringObjectFactory.java
Method: getClassInstance
Line: 245 - com/opensymphony/xwork2/spring/SpringObjectFactory.java:245:-1
at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:493)
at org.apache.struts2.dispatcher.ng.InitOperations.initDispatcher(InitOperations.java:74)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.init(StrutsPrepareAndExecuteFilter.java:57)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:260)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:105)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4700)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5340)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at com.opensymphony.xwork2.spring.SpringObjectFactory.getClassInstance(SpringObjectFactory.java:245)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.verifyResultType(XmlConfigurationProvider.java:608)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.addResultTypes(XmlConfigurationProvider.java:578)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.addPackage(XmlConfigurationProvider.java:534)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadPackages(XmlConfigurationProvider.java:295)
at org.apache.struts2.config.StrutsXmlConfigurationProvider.loadPackages(StrutsXmlConfigurationProvider.java:112)
at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:264)
at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:67)
at org.apache.struts2.dispatcher.Dispatcher.getContainer(Dispatcher.java:967)
at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:435)
at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:479)
... 14 more
4.5.2. 为什么会出现异常?
因为原来struts2的对象都是由xwork的对象处理,现在添加struts2-spring-plugin-2.3.20.jar文件,之后由spring接管了对原来struts2对象的创建
原因是在struts2-spring-plugin-2.3.20.jar文件里面有这个配置struts-plugin.xml
<struts>
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
<!-- Make the Spring object factory the automatic default -->
<constant name="struts.objectFactory" value="spring" />
<constant name="struts.class.reloading.watchList" value="" />
<constant name="struts.class.reloading.acceptClasses" value="" />
<constant name="struts.class.reloading.reloadConfig" value="false" />
<package name="spring-default">
<interceptors>
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
</interceptors>
</package>
</struts>
而spring容器并没有被启动
4.5.3. 在web.xml添加一个监听器,来实例化spring容器
<!-- 添加一个监听器,来实例化spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
4.5.4. 再次启动tomcat,又抛出异常
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext.xml]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330)
... 21 more
默认会去WEB-INF下面找配置文件,而此路径没有
4.5.5. 在web.xml添加一个上下文的初始化参数,来告诉spring从哪里加载配置文件
<!-- 添加一个上下文的初始化参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
4.5.6. 怎样快速找contextConfigLocation名字
找监听器ContextLoaderListener的父类ContextLoader
有一个常量配置
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
4.5.7. 不能加载jdbc.properties异常
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/jdbc.properties]
4.5.8. 修改spring的配置文件
<!-- 加载jdbc.properties -->
<!-- 跑web必须在前面添加classpath:前缀 -->
<context:property-placeholder location="classpath:jdbc.properties" />
4.5.9. 再次启动tomcat,没有异常
4.5.10. 访问http://localhost/product,出现404异常,
因为没有写jsp页面,此jsp页面和原来写JPA集成Struts2的jsp是一致的。
4.5.11. product.jsp
<body>
<table border="1">
<tr>
<th>编号</th>
<th>产品名称</th>
<!-- <th>产品类型名称</th> -->
<th>操作</th>
</tr>
<s:iterator value="#products">
<tr>
<td>${id}</td>
<td>${name}</td>
<%-- <td>${dir.name}</td> --%>
<td><a href="product_input?product.id=${id}">修改</a> <a
href="product_delete?product.id=${id}">删除</a></td>
</tr>
</s:iterator>
</table>
<a href="product_input">添加</a>
</body>
- 以同样的方式添加产品类型
5.1. 添加产品类型模型
模型ProductDir,修改Product添加@ManyToOne
写dao,service
5.2. 修改web.xml,解决延迟加载的异常
<!-- 添加关闭entityManger过滤器,必须在struts2过滤器之前 -->
<filter>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- struts.xml class属性配置原理
6.1. struts2加载配置文件顺序
struts2-core-2.3.20.jarorgapachestruts2default.properties
struts2-core-2.3.20.jarstruts-default.xml
struts2-spring-plugin-2.3.20.jarstruts-plugin.xml
...
classpath:struts.xml
6.2. struts2-spring-plugin-2.3.20.jar struts-plugin.xml
没有使用这个jar文件,struts2的对象创建是由xwork处理
添加这个这个jar文件之后struts2的对象就由spring创建
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
<!-- Make the Spring object factory the automatic default -->
<constant name="struts.objectFactory" value="spring" />
6.3. default.properties
默认情况下在struts.xml的action节点里面class属性写全限定类名是按照byName注入
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name
6.4. class属性
6.4.1. 方案1 建议使用,填写spring的bean id,
注意xml版本配置scope=’prototype’
注解版本配置@Scope(“prototype”)
好处1:可以使用小写的url地址,从使用通配符
<package name="default" namespace="/" extends="struts-default">
<!-- action代表一个控制器, name="对应的访问的url地址" method="返回action的方法" -->
<!-- spring集成struts之后,class可以写全限定类名,也可以写bean.id -->
<!-- *_* 请求的url_请求的方法 -->
<!-- 第1个*代表url地址,比如现在product,后面使用{1}来代替,url地址小写开头,正好对应action的bean.id的定义 -->
<!-- 第2个*代表访问action的方法,比如现在product_input,后面使用{2}来代替 -->
<action name="*_*" class="{1}Action" method="{2}">
<!-- 显示列表页面 -->
<result>/WEB-INF/views/{1}.jsp</result>
<!-- 显示添加获取修改页面 -->
<result name="input">/WEB-INF/views/{1}_input.jsp</result>
<!-- 保存或者删除之后进行重定向 -->
<result name="reload" type="redirectAction">{1}</result>
</action>
</package>
好处2:因为使用action是由spring管理
可以对action添加aop额外处理
坏处:spring配置文件写action的配置
@Controller//bean.id=productAction
@Scope("prototype")//配置多例
public class ProductAction extends ActionSupport {
6.4.2. 方案2 填写全限定类名,
如果填写全限定类名,则返回是小写url的地址,要求cn.itsource.ssj.action.ProductAction,类名对应要小写,不符合java命名规范;不能使用*_*通配符
@Autowired先按照类型,如果接口有2个实现,会报错
可以添加@Qualifier("productServiceImpl")名称来进行注入
public class ProductAction extends ActionSupport {
@Autowired
private IProductService productService;
- 课程总结
7.1. 重点
Ssj集成必须写熟练,1遍,2遍,3遍
7.2. 难点
Spring的配置文件
<?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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- jdbc.properties->dataSource->entityManagerFactory->dao->service->junit->action -->
<!-- 扫描dao、service、action组件 -->
<!-- @Repository, @Service, and @Controller,@Autowired,@PersistenceContext -->
<context:component-scan base-package="cn.itsource.ssj" />
<!-- 加载jdbc.properties -->
<!-- 跑web必须在前面添加classpath:前缀 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置连接池dataSource -->
<!-- destroy-method="close当前bean销毁的时候,会先调用close方法,关闭连接" -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- 依赖注入连接池需要的属性 -->
<!-- property name="是BasicDataSource的set方法,本质属性" -->
<!-- property value="是jdbc.properties配置文件的key" -->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter引入默认entityManagerFactory名称 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 1.注入DataSource -->
<property name="dataSource" ref="dataSource" />
<!-- 2.从哪个包去扫描@Entity,domain包 -->
<!-- public void setPackagesToScan(String... packagesToScan) { -->
<property name="packagesToScan" value="cn.itsource.ssj.domain" />
<!-- 3.配置JPA的实现 -->
<!-- private JpaVendorAdapter jpaVendorAdapter; -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter -->
<!-- private boolean showSql = false;是否显示sql语句 -->
<property name="showSql" value="true" />
<!-- private boolean generateDdl = false;是否建表 -->
<property name="generateDdl" value="true" />
<!-- private String databasePlatform;原来方言 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
</property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 开启注解事务管理 ,解析@Transactional注解 -->
<!-- transaction-manager="transactionManager"默认找bean.id=transactionManager事务管理器 -->
<tx:annotation-driven />
</beans>
- 常见异常
- javax.persistence.TransactionRequiredException: No transactional EntityManager available
事务没有配置成功
没有配置<tx:annotation-driven />
- No qualifying bean of type [cn.itsource.ssj.service.IProductService] is defined: expected single matching bean but found 2: productServiceImpl,productServiceImpl2
Could not autowire field: private cn.itsource.ssj.service.IProductService cn.itsource.ssj.action.ProductAction.productService; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of
type [cn.itsource.ssj.service.IProductService] is defined: expected single matching bean but found 2: productServiceImpl,productServiceImpl2
接口有2个实现