在myeclipse下,利用myeclipse自带的功能,为项目添加struts、jpa、spring的功能,可以极大的缩短开发时间。
本文是利用struts为表现层,jpa为持久层,spring为业务层,利用spring的依赖注入管理struts的action和jpa的entityManager、jpa的事务管理。
记录学习的脚步,!!!!!
1.在myeclipse下新建web project,名为SSJTest,并添加oracle 11g的驱动jar包,如下
2.为SSJTest项目添加spring功能,如下
点击install spring facet后,下一步如下
修改运行的目标环境后,其他的默认即可,一直点next,直至完成.
完成后 效果如下
打开web.xml文件中会发现 添加了如下配置信息
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
上面的ContextLoaderListener监听器用于初始化spring容器,此监听器会查找contextConfigLocation参数,然后获取其值作为spring容器的配置文件,如果不指定contextConfigLocation参数的话,则默认会查找
/WEB-INF/applicationContext.xml
下面的配置文件
3.给 SSJTest添加jpa功能,与添加sping功能很类似,并且利用myeclipse自带的jpa工程翻转,将oracle下面的stus表进行实体映射,并生成其数据访问层和数据访问层接口,
部分截图如下
jpa翻转
配置dao,并交由spring来进行管理
其他步骤,均下一步即可.最终效果
此时为了spring为jpa管理其事务,还需在StusDao上加入@Transactional注解,以便于spring容器进行事务管理
在添加struts功能之前,测试下spring与jpa的整合是否成功,于是添加junit test
测试代码:
package com.undergrowth; import static org.junit.Assert.*; import java.sql.Timestamp; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.UUID; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class StusDAOTest { private static ApplicationContext ac; private static IStusDAO isd; @BeforeClass public static void setUpBeforeClass() throws Exception { //初始化spring容器 ac=new ClassPathXmlApplicationContext("applicationContext.xml"); //获取stus的dao isd=(IStusDAO) ac.getBean("StusDAO"); } @Test public void testSave() { Stus stus=new Stus(UUID.randomUUID().toString(), "马化腾", 45.0, Timestamp.valueOf(new Date().toLocaleString())); isd.save(stus); } @Test public void testDelete() { fail("Not yet implemented"); } @Test public void testUpdate() { fail("Not yet implemented"); } @Test public void testFindAll() { List<Stus> listStus=isd.findAll(); for (Stus stus : listStus) { System.out.println(stus); } } }
先运行testSave然后运行testFindAll,控制台输出
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. [EL Info]: 2014-02-16 15:24:51.512--ServerSession(31211440)--EclipseLink, version: Eclipse Persistence Services - 2.4.2.v20130514-5956486 [EL Info]: connection: 2014-02-16 15:24:52.123--ServerSession(31211440)--file:/D:/learnsoftware/java/AndroidDevelop/myeclipse_2014_1_23/SSJTest/build/classes/_SSJTest login successful Stus [stuId=b31494c8-1806-49b5-b490-49e8a57e38e3, stuName=马化腾, stuAge=45.0, stuBirthday=2014-02-16 15:24:34.0]
表明spring与jpa合成没有问题
4.为其添加struts功能,添加struts功能之后,在struts.xml配置文件中加入以下配置信息
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <!-- 打开调试模式,获取更多的调试信息 --> <constant name="struts.devMode" value="true"></constant> <!-- 使用spring容器来管理struts的action --> <constant name="struts.objectFactory" value="spring"></constant> <package name="stus" namespace="/stus" extends="struts-default"> <!-- 使用*占位符来扩展多个方法 --> <action name="stus_*" class="stusAction" method="{1}"> <result name="list">/WEB-INF/page/stusList.jsp</result> </action> </package> </struts>
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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" xmlns:tx="http://www.springframework.org/schema/tx"> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="SSJTest" /> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 用于jpa的事务管理 --> <tx:annotation-driven transaction-manager="transactionManager" /> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> </bean> <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"> </bean> <bean id="StusDAO" class="com.undergrowth.StusDAO"></bean> <bean id="stusAction" class="com.undergrowth.action.StusAction"> <!-- 注入数据访问层 --> <property name="stusDAO" ref="StusDAO"></property> </bean> </beans>
中转的action为
package com.undergrowth.action; import java.util.List; import com.undergrowth.IStusDAO; import com.undergrowth.Stus; public class StusAction { private List<Stus> listPerson; private IStusDAO stusDAO; public IStusDAO getStusDAO() { return stusDAO; } public void setStusDAO(IStusDAO stusDAO) { this.stusDAO = stusDAO; } public List<Stus> getListPerson() { return listPerson; } public void setListPerson(List<Stus> listPerson) { this.listPerson = listPerson; } public String getList() { this.listPerson=stusDAO.findAll(); return "list"; } }
页面前段显示的界面,如下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>信息列表</title> </head> <body> <s:iterator value="listPerson"> <s:property/> </s:iterator> </body> </html>
package com.undergrowth; import java.sql.Timestamp; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; /** * Stus entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "STUS", schema = "UNDER") public class Stus implements java.io.Serializable { // Fields private String stuId; private String stuName; private Double stuAge; private Timestamp stuBirthday; // Constructors /** default constructor */ public Stus() { } /** full constructor */ public Stus(String stuId, String stuName, Double stuAge, Timestamp stuBirthday) { this.stuId = stuId; this.stuName = stuName; this.stuAge = stuAge; this.stuBirthday = stuBirthday; } // Property accessors @Id @Column(name = "STU_ID", unique = true, nullable = false, length = 50) public String getStuId() { return this.stuId; } public void setStuId(String stuId) { this.stuId = stuId; } @Column(name = "STU_NAME", nullable = false, length = 30) public String getStuName() { return this.stuName; } public void setStuName(String stuName) { this.stuName = stuName; } @Column(name = "STU_AGE", nullable = false, precision = 0) public Double getStuAge() { return this.stuAge; } public void setStuAge(Double stuAge) { this.stuAge = stuAge; } @Column(name = "STU_BIRTHDAY", nullable = false, length = 7) public Timestamp getStuBirthday() { return this.stuBirthday; } public void setStuBirthday(Timestamp stuBirthday) { this.stuBirthday = stuBirthday; } @Override public String toString() { return "Stus [stuId=" + stuId + ", stuName=" + stuName + ", stuAge=" + stuAge + ", stuBirthday=" + stuBirthday + "]"; } }
数据访问层StusDao.java
package com.undergrowth; import java.sql.Timestamp; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; /** * A data access object (DAO) providing persistence and search support for Stus * entities. Transaction control of the save(), update() and delete() operations * can directly support Spring container-managed transactions or they can be * augmented to handle user-managed Spring transactions. Each of these methods * provides additional information for how to configure it for the desired type * of transaction control. * * @see com.undergrowth.Stus * @author MyEclipse Persistence Tools */ @Transactional @Repository public class StusDAO implements IStusDAO { private static final Log logger = LogFactory.getLog(StusDAO.class); // property constants public static final String STU_NAME = "stuName"; public static final String STU_AGE = "stuAge"; private EntityManager entityManager; @PersistenceContext public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } private EntityManager getEntityManager() { return entityManager; } /** * Perform an initial save of a previously unsaved Stus entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * <p> * User-managed Spring transaction example: * * <pre> * TransactionStatus txn = txManager * .getTransaction(new DefaultTransactionDefinition()); * StusDAO.save(entity); * txManager.commit(txn); * </pre> * * @see <a href = * "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring * container-managed transaction examples</a> * @param entity * Stus entity to persist * @throws RuntimeException * when the operation fails */ public void save(Stus entity) { logger.info("saving Stus instance"); try { getEntityManager().persist(entity); logger.info("save successful"); } catch (RuntimeException re) { logger.error("save failed", re); throw re; } } /** * Delete a persistent Stus entity. This operation must be performed within * the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * <p> * User-managed Spring transaction example: * * <pre> * TransactionStatus txn = txManager * .getTransaction(new DefaultTransactionDefinition()); * StusDAO.delete(entity); * txManager.commit(txn); * entity = null; * </pre> * * @see <a href = * "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring * container-managed transaction examples</a> * @param entity * Stus entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Stus entity) { logger.info("deleting Stus instance"); try { entity = getEntityManager().getReference(Stus.class, entity.getStuId()); getEntityManager().remove(entity); logger.info("delete successful"); } catch (RuntimeException re) { logger.error("delete failed", re); throw re; } } /** * Persist a previously saved Stus entity and return it or a copy of it to * the sender. A copy of the Stus entity parameter is returned when the JPA * persistence mechanism has not previously been tracking the updated * entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * <p> * User-managed Spring transaction example: * * <pre> * TransactionStatus txn = txManager * .getTransaction(new DefaultTransactionDefinition()); * entity = StusDAO.update(entity); * txManager.commit(txn); * </pre> * * @see <a href = * "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring * container-managed transaction examples</a> * @param entity * Stus entity to update * @return Stus the persisted Stus entity instance, may not be the same * @throws RuntimeException * if the operation fails */ public Stus update(Stus entity) { logger.info("updating Stus instance"); try { Stus result = getEntityManager().merge(entity); logger.info("update successful"); return result; } catch (RuntimeException re) { logger.error("update failed", re); throw re; } } public Stus findById(String id) { logger.info("finding Stus instance with id: " + id); try { Stus instance = getEntityManager().find(Stus.class, id); return instance; } catch (RuntimeException re) { logger.error("find failed", re); throw re; } } /** * Find all Stus entities with a specific property value. * * @param propertyName * the name of the Stus property to query * @param value * the property value to match * @return List<Stus> found by query */ @SuppressWarnings("unchecked") public List<Stus> findByProperty(String propertyName, final Object value) { logger.info("finding Stus instance with property: " + propertyName + ", value: " + value); try { final String queryString = "select model from Stus model where model." + propertyName + "= :propertyValue"; Query query = getEntityManager().createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } catch (RuntimeException re) { logger.error("find by property name failed", re); throw re; } } public List<Stus> findByStuName(Object stuName) { return findByProperty(STU_NAME, stuName); } public List<Stus> findByStuAge(Object stuAge) { return findByProperty(STU_AGE, stuAge); } /** * Find all Stus entities. * * @return List<Stus> all Stus entities */ @SuppressWarnings("unchecked") public List<Stus> findAll() { logger.info("finding all Stus instances"); try { final String queryString = "select model from Stus model"; Query query = getEntityManager().createQuery(queryString); return query.getResultList(); } catch (RuntimeException re) { logger.error("find all failed", re); throw re; } } public static IStusDAO getFromApplicationContext(ApplicationContext ctx) { return (IStusDAO) ctx.getBean("StusDAO"); } }
5.测试效果如下
数据库中截图
以上步骤即是struts、spring、jpa的集成,当然相应的添加、修改、删除操作也是类似的,就不写了
6.遇到的问题
严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stusAction' defined in class path resource [applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type '$Proxy21 implementing com.undergrowth.IStusDAO,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'com.undergrowth.StusDAO' for property 'stusDAO'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [$Proxy21 implementing com.undergrowth.IStusDAO,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.undergrowth.StusDAO] for property 'stusDAO': no matching editors or conversion strategy found at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:385) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:284) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633) at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1113) at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1671) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:722) Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type '$Proxy21 implementing com.undergrowth.IStusDAO,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'com.undergrowth.StusDAO' for property 'stusDAO'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [$Proxy21 implementing com.undergrowth.IStusDAO,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.undergrowth.StusDAO] for property 'stusDAO': no matching editors or conversion strategy found at org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:485)
上面的错误是说在StusAction进行依赖注入的时候stusDao的类型不匹配,原因在于spring容器在进行注入的时候,为stusDao生成代理对象的时候,采用的是jdk的动态代理,但是jdk的动态代理有一个前提就是,目标对象和代理对象有一个共同的接口,所以会造成类型不匹配
解决方案如下: