Hibernate 与 Spring

专家说:冬眠完了,春天来了。

其实这两个玩意做的事情很简单,一个(hibernate)是解决关系数据库(relational database)到对象模型(object model)的映射(ORM,object-relational mapping),一个(spring)是为了让 business logic 完全建立在抽象的接口之上,而将具体类插入到逻辑中。为了编译方便,我们使用 maven 来进行管理。

maven 的例子

上手的话,可以参看着一个例子

  • 生成目录结构
    mvn archetype:generate -DgroupId=com.wordpress.remonstrate.sandbox -DartifactId=helloworld \
    -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
  • 修改代码,进行 package
    mvn package
  • 执行
    java -cp target/helloworld-1.0-SNAPSHOT.jar com.wordpress.remonstrate.sandbox.App
  • 清理
    mvn clean

一般说来 maven 有如下几个 phase:

  • validate
  • compile
  • test
  • package
  • integration-test
  • verify
  • install
  • deploy

我估计现在先熟悉前面到 package 这部分就行。毕竟公司自己的 infrastructure 已经全部 handle 了整个部分。而如果我自己练习写写 code 什么的,到 package 能自己玩玩就够用了。有空再看别的部分了。

spring

我们就拿以上例子来看看如何利用 spring 的框架实现,我们参看这个网页。我们这里相当于仅仅利用 spring framework 里面最简单的部分,即 IoC container 对使用的对象进行配置。当然 spring 显然也不是一个那么容易轻松用完所有功能的 framework,我们后面再来学习其他的有意思的部分。为了在 maven 里面使用 spring,我们可以如下修改 pom.xml:

<project>
  <!-- many stuffs -->
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring</artifactId>
      <version>2.5.6</version>
    </dependency>
    <!-- other dependencies -->
  </dependencies>
  <build>
    <plugins>
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <addClasspath>true</addClasspath>
            <mainClass>com.amazon.sandbox.App</mainClass>
          </manifest>
        </archive>
      </configuration>
      </plugin>
      <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <configuration>
        <descriptorRefs>
              <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
      </plugin>
    </plugins>
  </build>

后面那个 plugin 的作用在于可以使用

mvn asembly:single

将前面产生的结果和依赖的 jar 打成一个 jar,这样便于独立的执行我们的简单的程序。然后我们来看看例子里面需要一些什么。我们保留 App.java 作为后面的入口程序。我们希望实现一个 Greetable 的 interface,这用来帮助我们实现业务逻辑。定义如下:

package com.wordpress.remonstrate.sandbox;

public interface Greetable {
    public void greet (String who) ;
}

我们很 sb 的实现一个前缀型一个后缀型的 greeter,他们继承于

package com.wordpress.remonstrate.sandbox;

public class FixGreeter {
    protected String fix ;
    public void setFix (String s) {
        fix = s ;
    }
}

下面是我们两个具体的实现:

package com.wordpress.remonstrate.sandbox;

public class PrefixGreeter extends FixGreeter implements Greetable {
    public void greet (String who) {
        System.out.println (fix + who) ;
    }
}

public class SuffixGreeter extends FixGreeter implements Greetable {
    public void greet (String who) {
        System.out.println (who + fix) ;
    }
}

有了这两个之后我们就可以看看怎么写一个简单的 IoC container 的配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-b
eans-2.5.xsd">
  <bean id="greeter" class="com.wordpress.remonstrate.sandbox.PrefixGreeter">
    <property name="fix" value="hello " />
  </bean>
</beans>

我们将其放在 [root]/src/main/resources/spring-conf.xml,如下修改 App.java

package com.wordpress.remonstrate.sandbox;

import org.springframework.context.ApplicationContext ;
import org.springframework.context.support.ClassPathXmlApplicationContext ;

public class App {
    public static void main( String[] args ) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext ("spring-conf.xml") ;
        Greetable greeter = (Greetable) context.getBean ("greeter") ;
        greeter.greet ("world") ;
    }
}

之后执行我们的 jar 文件即可。如果我们修改对应的 XML 就能直接切换业务的实现者从 PrefixGreeter 切换到 SuffixGreeter 了。

hibernate

我们先简单的叙述一下通过 hibernate 处理事务的逻辑,然后结合这篇简介给出一个简单的例子。简单的说,

  • hibernate 通过一个配置文件(xml)决定连接的数据库
  • 实现一个 POJO(一直觉得 Java 的 POJO 挺傻的,既然都 get/set 了,还不如 C++ 里面的 struct 默认都是 public 的),并将其关联到某个 table,我们有两种策略干这件事情:通过一个 XML 文件描述这个对象与 table 里面各个 schema 的关系;使用 annotation(这样就不要 xml 了!)
  • 实际操作中我们需要初始化一个 thread-safe 的 session factory(一个进程里面一般只需要一个就行了)
  • 数据库操作以从 SessionFactory 获得 session 开始,这个 Session 对象提交所有的工作,或者用 Java 语法(所谓的 HQL?)或者直接提交 SQL 语句。

这样一来我们使用 spring + hibernate 形式上配合一下,我们通过 spring 为我们的程序提供 session factory,然后开始一个简单的 DB 会话。我们的 schema 也很简单:id、salary 和 age。随机产生一些数据,然后提交查询返回结果到标准输出。我们为了使用 hibernate + h2 这个 in memory 的数据库,需要额外增加两个依赖关系:

<project>
  <!-- many stuffs -->
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring</artifactId>
      <version>2.5.6</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.1.6.Final</version>
    </dependency>

    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.2.145</version>
    </dependency>

    <!-- the rest omitted -->
</project>

我们需要一个 hibernate 的 XML,这个 XML 是 hibernate 用来构造自己的 session factory 的,我们可以用 org.hibernate.cfg.Configuration 来读取并且完成构造。正是因为这个东西不是用 Spring 来做的,也导致了 Spring 或者别的什么 framework 里面必须解决这个问题,我们这里偷懒只用 Spring 管理 Configuration 对象了。

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
    <property name="connection.driver_class">org.h2.Driver</property>
    <property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE\
</property>
    <property name="connection.username">sa</property>
    <property name="connection.password"/>
    <property name="connection.pool_size">1</property>
    <property name="dialect">org.hibernate.dialect.H2Dialect</property>
    <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheP\
rovider</property>
    <property name="show_sql">true</property>
    <property name="hbm2ddl.auto">create</property>
    <mapping class="com.wordpress.remonstrate.sandbox.EmployeeInfo" />
  </session-factory>
</hibernate-configuration>

这个文件名默认一般叫 hibernate.cfg.xml,如果不喜欢就需要用 spring 的 constrctor injection 来传递这个参数了。之后我们建立一个 EmployeeInfo 的类与 Employees 这张表对应起来。

package com.wordpress.remonstrate.sandbox ;

import javax.persistence.Entity ;
import javax.persistence.Table ;
import javax.persistence.Id ;
import javax.persistence.GeneratedValue ;
import javax.persistence.Column ;

import org.hibernate.annotations.GenericGenerator ;

@Entity
@Table (name = "Employees")
public class EmployeeInfo {
    private Long id ;
    private int age ;
    private Double salary ;

    public EmployeeInfo () {}

    public EmployeeInfo (int a, double b) {
        age = a ; salary = new Double (b) ;
    }

    @Id
    @GeneratedValue (generator = "increment")
    @GenericGenerator (name = "increment", strategy = "increment")
    public Long getId () {
        return id ;
    }

    private void setId (Long i) {
        id = i ;
    }

    @Column (name = "Age")
    public int getAge () {
        return age ;
    }

    public void setAge (int a) {
        age = a;
    }

    public Double getSalary () {
        return salary ;
    }

    public void setSalary (Double s) {
        salary = s ;
    }
}

嗯,后面我们两步都写在 App.java 里面了。

package com.wordpress.remonstrate.sandbox ;

import org.springframework.context.ApplicationContext ;
import org.springframework.context.support.ClassPathXmlApplicationContext ;

import org.hibernate.cfg.Configuration ;
import org.hibernate.SessionFactory ;
import org.hibernate.Session ;

import java.util.List ;

public class App {
    public static void main( String[] args ) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext ("spring-conf.xml") ;
        Configuration conf = (Configuration) context.getBean ("conf") ;
        SessionFactory sf = conf.configure ().buildSessionFactory () ;

        // save data
        {
            Session s = sf.openSession () ;
            s.beginTransaction () ;
            for (int i = 0 ; i < 100 ; ++ i) {
                s.save (new EmployeeInfo ((int)(Math.random () * 40 + 16),
                                          Math.random () * 1000 + 400)) ;
                System.out.print ('.') ;
            }
            s.getTransaction ().commit () ;
            s.close () ;
            System.out.println (" saved") ;
        }

        // submit some queries
        {
            Session s = sf.openSession () ;
            s.beginTransaction () ;
            List result = s.createQuery ("FROM EmployeeInfo AS info WHERE info.\
age < 20").list () ;
            for (EmployeeInfo info : (List<EmployeeInfo>) result)
                System.out.println ("ID: " + info.getId () +
                                    ", Age: " + info.getAge () +
                                    ", Salary: $" + info.getSalary ()) ;
            s.getTransaction ().commit () ;
            s.close () ;
        }
    }
}

值得注意的是 createQuery 那部分提交的是 HQL 而不是 SQL,当然我们可以用 prepared statement 提交 SQL 的,看文档就知道怎么做了。

其他

另外有一种 dependency injection 叫 interface injection,spring 并没有实现,另外一个叫 Avalon 的实现了这个功能,但这个已经结束了,现在不知道是不是 Excalibur 在做?

——————
And the man came into the house: and he ungirded his camels, and gave straw and provender for the camels, and water to wash his feet, and the men’s feet that were with him.

Advertisements
Hibernate 与 Spring

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s