虽然现在Spring已经步入了3.x的时代,这里就简单介绍一下SSH集成快速快发,Spring的IOC和Hibernate使用了Annotation,Spring的AOP用了xml配置的方式,就是声明式事务的配置方式:
1.导入jar:
2.搭建框架:
a.准备资源文件jdbc.properties
jdbc.driverClass = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/hibernate?userUnicode=true&characterEncoding=UTF-8 jdbc.userName = root jdbc.password =ysjian
b.配置applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <!-- 这里特别的注意,xsi:schemaLocation中要有上面的信息对应的地址 --> <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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- 使用注解的配置前提 --> <context:annotation-config /> <!-- 指定在com.ssh下所有的包去匹配,需要在特定的bean中配置注解 --> <context:component-scan base-package="com.ssh"/> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:jdbc.properties</value> </property> </bean> <!-- 配置数据源:dataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClass}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.userName}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置SessionFactory,注入数据源dataSource --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <!-- 注入数据源dataSource --> <property name="dataSource" ref="dataSource" /> <!-- 注入配置文件 --> <property name="packagesToScan"> <list> <value>com.ssh.entity</value> </list> </property> <!-- 注入hibernate常用的属性 --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialet">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <!--<prop key="hibernate.format_sql">true</prop> --> </props> </property> </bean> <!-- 注入HibernateTemplate,建议使用组合的方式 --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!--配置声明式事务 ,将事务从DAO转移到BIZ--> <!-- 配置事务管理器 ,注入SessionFactory--> <bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 定义事务通知 --> <tx:advice id="txAdvice" transaction-manager="hibernateTransactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="*" propagation="SUPPORTS" /> </tx:attributes> </tx:advice> <!-- 配置AOP --> <aop:config> <!-- 定义一个切入点(断言) --> <aop:pointcut id="bizMethods" expression="execution(* com.ssh.biz..*.*(..))" /> <!-- 定义通知者 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethods" /> </aop:config> </beans>
c..配置struts.xml,这里只配了一个Action
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.i18n.encoding" value="GBK"></constant> <package name="struts2" extends="struts-default"> <!-- 使用class="spring中Action的id" 用的是伪类 --> <action name="user_*" class="com.ssh.action.UserAction" method="{1}"> <result name="success" type="redirectAction">user_doList</result> <result name="list">/list.jsp</result> <result name="detail">/update.jsp</result> <result name="login" type="redirect">/login.jsp</result> </action> </package> </struts>
d.配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 监听器,初始化Spring容器,读取applicationContext.xml配置文件的信息 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <!-- <param-value> /WEB-INF/applicationContext-*.xml, classpath*:applicationContext-*.xml </param-value> --> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--配置OSIV过滤器,解决在页面出现懒加载取不出数据的问题 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置struts2核心控制器 --> <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>*.action</url-pattern> <url-pattern>*.jsp</url-pattern> </filter-mapping> </web-app>
3.模块设计,编写model
a.实体(Userinfo)
import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="userinfo") public class Userinfo implements Serializable { private static final long serialVersionUID = 3171941410139893214L; private int uid; private String uname; private String upass; @Id @GeneratedValue public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String getUpass() { return upass; } public void setUpass(String upass) { this.upass = upass; } @Override public String toString() { return "Userinfo [uid=" + uid + ", uname=" + uname + ", upass=" + upass + "]"; } }
b.DAO(UserinfoDao&UserinfoDaoImpl)
import java.util.List; import javax.annotation.Resource; import org.springframework.dao.DataAccessException; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.stereotype.Component; import com.ssh.dao.UserinfoDao; import com.ssh.entity.Userinfo; @Component("userDao") public class UserinfoDaoImpl implements UserinfoDao { private HibernateTemplate hibernateTemplate; @Resource public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } /** * 新增方法 */ public void addUser(Userinfo user) { try { hibernateTemplate.save(user); } catch (DataAccessException e) { throw e; } } /** * 登录 */ public Userinfo login(Userinfo user) { String hql = "from Userinfo u where u.uname=? and u.upass=?"; List<Userinfo> list = hibernateTemplate.find(hql, new String[] { user.getUname(), user.getUpass() }); Userinfo loginUser = null; if (list != null && list.size() != 0) { loginUser = list.get(0); } return loginUser; } /** * 删除 */ public void deleteUser(Userinfo user) { try { hibernateTemplate.delete(user); } catch (DataAccessException e) { throw e; } } /** * 根据编号查询 */ public Userinfo getUserById(int uid) { return (Userinfo) hibernateTemplate.get(Userinfo.class, uid); } /** * 查询所有 */ public List<Userinfo> getAllUsers() { String hql = "from Userinfo"; // return hibernateTemplate.loadAll(Userinfo.class); return hibernateTemplate.find(hql); } /** * 更新 */ public void updateUser(Userinfo user) { try { hibernateTemplate.update(user); } catch (DataAccessException e) { throw e; } } }
c.BIZ(UserinfoBzi&UserinfoBizImpl)
import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Component; import com.ssh.biz.UserinfoBiz; import com.ssh.dao.UserinfoDao; import com.ssh.entity.Userinfo; @Component("userBiz") public class UserinfoBizImpl implements UserinfoBiz { private UserinfoDao userinfoDao; @Resource(name="userDao") public void setUserinfoDao(UserinfoDao userinfoDao) { this.userinfoDao = userinfoDao; } public void addUser(Userinfo user) { userinfoDao.addUser(user); } public Userinfo login(Userinfo user) { return userinfoDao.login(user); } public void deleteUser(Userinfo user) { userinfoDao.deleteUser(user); } public Userinfo getUserById(int uid) { return userinfoDao.getUserById(uid); } public List<Userinfo> getAllUsers() { return userinfoDao.getAllUsers(); } public void updateUser(Userinfo user) { userinfoDao.updateUser(user); } }
d.Action(BasicAction&UserAction)
import java.util.Map; import org.apache.struts2.interceptor.RequestAware; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionSupport; public class BasicAction extends ActionSupport implements RequestAware,SessionAware{ private static final long serialVersionUID = -978999257562885869L; protected Map<String,Object> request; protected Map<String,Object> session; public void setRequest(Map<String, Object> request) { this.request = request; } public void setSession(Map<String, Object> session) { this.session = session; } }
mport java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Component; import com.ssh.biz.UserinfoBiz; import com.ssh.entity.Userinfo; @Component("userAction") public class UserAction extends BasicAction { private static final long serialVersionUID = -7076569534584788161L; private UserinfoBiz userBiz; private Userinfo user; @Resource(name="userBiz") public void setUserBiz(UserinfoBiz userBiz) { this.userBiz = userBiz; } public Userinfo getUser() { return user; } public void setUser(Userinfo user) { this.user = user; } /** * 登录 * @return * @throws Exception */ public String doLogin() throws Exception { Userinfo loginUser = userBiz.login(user); if (loginUser != null) { session.put("loginUser", loginUser); return this.SUCCESS; } return this.LOGIN; } public String doList() throws Exception { List<Userinfo> list = userBiz.getAllUsers(); request.put("list", list); return "list"; } /** * 登录 * * @return * @throws Exception */ public String doAdd() throws Exception { userBiz.addUser(user); return this.SUCCESS; } /** * 查找个人详细信息系 * @return * @throws Exception */ public String doDetail() throws Exception { user = userBiz.getUserById(user.getUid()); request.put("user", user); return "detail"; } /** * 更新个人详细信息系 * @return * @throws Exception */ public String doUpdate() throws Exception { userBiz.updateUser(user); return this.SUCCESS; } /** * 登录 * * @return * @throws Exception */ public String doDelete() throws Exception { user = userBiz.getUserById(user.getUid()); userBiz.deleteUser(user); return this.SUCCESS; } }
大体的写好了,可以进行测试了,这里就不写了,SSH继承,简单的模式,在项目中可以再此基础上扩展。
说说几个问题:
1.那个OpenSessionInViewFilter过滤器是用来解决在jsp页面取数据是Session关闭后无法取出懒加载关联的对象,建议配上。
<!--配置OSIV过滤器,解决在页面出现懒加载取不出数据的问题 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2.中文乱码问题,可以再Struts2中的struts.xml配置文件中配置一个常量,Spring提供了一个过滤器,但建议使用struts2中的常量。
<constant name="struts.i18n.encoding" value="GBK" />
Spring的Filter:
<!-- struts1中的乱码解决问题,在struts2中只需要设置一个常量 --> <filter> <filter-name>encoding</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>GBK</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3.如果在applicationContext.xml中没有配置声明式事务,那么在页面中比如注册和删除修改等操作会出现错误:
错误如下:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
简单说一下这个原因:因为如果没有在applicationContext.xml中配置声明式事务的话,而OpenSessionInViewFilter在web.xml中有配置,OpenSessionInViewFilte此时默认对事物的管理是read-only的,所以修改操作会出现问题,而读取没有问题
a.可以去掉OpenSessionInViewFilter,但不建议这么做.
b.将声明式事务配上,不搞特殊化,呵呵。
另外我还发现,如果不配置声明式事务,在测试时,同样能给数据库保存数据,这个和之前Spring与Hibernate的集成有点区别,HibernateTemplate带来的好处,但还是不建议不配置声明式事务,如果不配置,又回到那个转账的问题了。