《跟我一起做J2EE版Blog–jPress》4(搭建marven下的Spring和Hibernate)
jPress使用SSH的最新版本来开发,分别是Struts2.3.1,Spring3.1.1和Hibernate4.1.1,此外还将涉及Freemarker模板引擎来进行显示层的制作。在marven中配置SSH环境只需要编写好pom.xml文件即可,我们利用myeclipse为项目添加SSH特性的时候不要引入任何jar包。marven在build项目时会帮助我们自动引入所需的各种jar包,如果本地库中没有所需jar包,marven会从它的中央库中下载过来保存进本地库。上一章中我已经贴出了我目前的pom.xml文件,随着项目的推进,期间可能需要引入其他jar包,除非有重大的改变,我这里就不再对pom.xml赘述了。JAVA项目的设计层次很重要,层次的引入会降低项目的耦合度,今后开发起别的项目起来会非常顺畅,这一点跟asp和php又很大不同,我不否认PHP的灵活,但是php在国内的开发者手里完全是面向过程的语言,成百上千个全局函数让我很不适应。JAVA项目的开发自始自终散发出一种面向对象和解藕的光辉,这是让我感到愉悦的。
Spring的核心思想是解藕,对象的初始化在xml中描述出来了,应用程序不再需要new出一个对象并对其进行各种初始化,而是从Spring容器中取出这些初始化好的对象。下面来配置Spring,使用myeclipse添加spring特性后会得到一个applicationContext.xml,Spring会读取这个配置文件初始化一些bean以供我们注入。我先贴出我修改过的applicationContext.xml文件:
今天先写到这里,下一章我们将来设计数据库接口,原则上的构架是:一个数据库model会对应一个DAO,并有一个Impl类来实现这个接口,将这个Impl使用Spring的annotation注解为componet供将来的业务层注入使用。实际操作过程中,我们会首先设计一个通用接口BaseDao,在通用接口中使用JAVA泛型定义一组重用率非常高的增删改查操作,我们会有一个通用接口的通用抽象实现BaseDaoImpl,各个DAO接口继承自这个通用数据接口BaseDao,各个DAO的Impl继承自BaseDaoImpl并实现各自的接口,这样说上去比较绕,下一章实际操作起来其实层次感非常清晰。
Spring的核心思想是解藕,对象的初始化在xml中描述出来了,应用程序不再需要new出一个对象并对其进行各种初始化,而是从Spring容器中取出这些初始化好的对象。下面来配置Spring,使用myeclipse添加spring特性后会得到一个applicationContext.xml,Spring会读取这个配置文件初始化一些bean以供我们注入。我先贴出我修改过的applicationContext.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:p="http://www.springframework.org/schema/p" xmlns:content="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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 注解扫描包 --> <content:annotation-config/> <content:component-scan base-package="com.flyding.jpress"/> <!-- 使用占位符读取配置信息 --> <bean id="placeHolder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"> <value>classpath:config.properties</value> </property> </bean> <!-- 数据源 使用hibernate所建议的c3p0数据源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.psw}" /> <property name="initialPoolSize"><value>${hibernate.c3p0.initialPoolSize}</value></property> <property name="minPoolSize"><value>${hibernate.c3p0.minPoolSize}</value></property> <property name="maxPoolSize"><value>${hibernate.c3p0.maxPoolSize}</value></property> <property name="maxIdleTime"><value>${hibernate.c3p0.timeout}</value></property> <property name="maxStatements"><value>${hibernate.c3p0.max_statement}</value></property> <property name="acquireIncrement"><value>${hibernate.c3p0.acquireIncrement}</value></property> </bean> <!-- hibernate4中的LocalSessionFactoryBean直接提供了annotation支持,这一点跟hibernate3有所不同 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="javax.persistence.validation.mode">none</prop> </props> </property> <property name="packagesToScan"> <value>com.flyding.jpress.model</value> </property> </bean> <!-- 事务管理 --> <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 植入点 --> <aop:config> <!-- 定义一个植入点 --> <aop:pointcut id="daoTransactionPt" expression="execution(public * com.flyding.jpress.dao.impl.*.*(..))"/> <!-- 配置这个植入点将参考怎样的智者 --> <aop:advisor pointcut-ref="daoTransactionPt" advice-ref="daoTransactionAdvice"/> </aop:config> <!-- 配置事务智者 --> <tx:advice id="daoTransactionAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 用于注解方式配置事务 --> <tx:annotation-driven transaction-manager="txManager"/> </beans>这份配置文件是我从上一个项目中copy过来的,复用率非常高,稍作修改就能在新的项目中使用。基于SSH的开发流程非常清晰,不会让人无从下手,从Spring的配置文件开始,我们就已经看到是Spring在集成Hibernate了。将经常需要修改的数据源信息分离在独立的config.properties文件中,使用placeHolder占位符读取配置信息并将其填入。下面是一份config.properties文件:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/jpress jdbc.username=root jdbc.psw=******** hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.show_sql=true; hibernate.c3p0.initialPoolSize=10 hibernate.c3p0.minPoolSize=10 hibernate.c3p0.maxPoolSize=50 hibernate.c3p0.timeout=300 hibernate.c3p0.max_statement=50 hibernate.c3p0.acquireIncrement=5接下来需要着手编写Hibernate的model了,我习惯于使用annotation配置ORM映射关系,数据库中一共有10张表,其中有两个表是多对多的中间表,这两表是不产生模型的。所以我们一共需要生成8个java文件,并在文件中使用注解的方式描述类与数据库表的关系,myeclipse为我们提供了直接生成的工具,下面是一份由myeclipse生成的model:
package com.flyding.jpress.model; import java.sql.Timestamp; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; /** * User entity. @author flyding.com */ @Entity @Table(name = "jp_user", catalog = "jpress") public class User implements java.io.Serializable { private static final long serialVersionUID = 9010981240846378504L; // Fields private Long id; private String username; private String nickname; private String password; private String email; private Timestamp registerDt; private String userUrl; private Integer userLevel; private Set<Entry> entries = new HashSet<Entry>(0); private Set<UserMeta> userMetas = new HashSet<UserMeta>(0); // Constructors /** default constructor */ public User() { } /** minimal constructor */ public User(String username, String password, String email, Timestamp registerDt, Integer userLevel) { this.username = username; this.password = password; this.email = email; this.registerDt = registerDt; this.userLevel = userLevel; } /** full constructor */ public User(String username, String nickname, String password, String email, Timestamp registerDt, String userUrl, Integer userLevel, Set<Entry> entries, Set<UserMeta> userMetas) { this.username = username; this.nickname = nickname; this.password = password; this.email = email; this.registerDt = registerDt; this.userUrl = userUrl; this.userLevel = userLevel; this.entries = entries; this.userMetas = userMetas; } // Property accessors @Id @GeneratedValue @Column(name = "id", unique = true, nullable = false) public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } @Column(name = "username", nullable = false, length = 50) public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } @Column(name = "nickname", length = 50) public String getNickname() { return this.nickname; } public void setNickname(String nickname) { this.nickname = nickname; } @Column(name = "password", nullable = false, length = 50) public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } @Column(name = "email", nullable = false, length = 50) public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } @Column(name = "register_dt", nullable = false, length = 19) public Timestamp getRegisterDt() { return this.registerDt; } public void setRegisterDt(Timestamp registerDt) { this.registerDt = registerDt; } @Column(name = "user_url", length = 50) public String getUserUrl() { return this.userUrl; } public void setUserUrl(String userUrl) { this.userUrl = userUrl; } @Column(name = "user_level", nullable = false) public Integer getUserLevel() { return this.userLevel; } public void setUserLevel(Integer userLevel) { this.userLevel = userLevel; } @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user") public Set<Entry> getEntries() { return this.entries; } public void setEntries(Set<Entry> entries) { this.entries = entries; } @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user") public Set<UserMeta> getUserMetas() { return this.userMetas; } public void setUserMetas(Set<UserMeta> userMetas) { this.userMetas = userMetas; } }生成8个类似的数据层model后,我们的项目工程如图所示:
今天先写到这里,下一章我们将来设计数据库接口,原则上的构架是:一个数据库model会对应一个DAO,并有一个Impl类来实现这个接口,将这个Impl使用Spring的annotation注解为componet供将来的业务层注入使用。实际操作过程中,我们会首先设计一个通用接口BaseDao,在通用接口中使用JAVA泛型定义一组重用率非常高的增删改查操作,我们会有一个通用接口的通用抽象实现BaseDaoImpl,各个DAO接口继承自这个通用数据接口BaseDao,各个DAO的Impl继承自BaseDaoImpl并实现各自的接口,这样说上去比较绕,下一章实际操作起来其实层次感非常清晰。