《跟我一起做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文件:
<?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并实现各自的接口,这样说上去比较绕,下一章实际操作起来其实层次感非常清晰。

posted on 2012-03-16 18:37  newflypig  阅读(697)  评论(0编辑  收藏  举报

导航