顯示具有 Hibernate 標籤的文章。 顯示所有文章
顯示具有 Hibernate 標籤的文章。 顯示所有文章

20 10月 2009

hibernate3-maven-plugin直接使用Spring AnnotationSessionFactoryBean

新公司有自己的一套Framework,並自行開發一套code gen的工具,稍為喚起了我曽經用過Hibernate Tools的記憶,一方面是因為用了JDK5的Generic後,要寫的Code變少,另一方面也是因為如果有修改,code gen出來的東西比較不易配合更動,所以也沒有納入我常用的工具之一。不過既然想起了Hibernate tools,那就再用用看有沒有什麼改變,或許會有驚喜吧。

不過似乎沒有…特別是不能直接用Spring Configuration Xml中已經訂好的設定,一定要另訂hibernate.cfg.xml,這就又讓我難過了一下,再發現居然連hibernate3-maven-plugin也無法直接使用Spring 裡訂好的AnnotationSessionFactoryBean或LocalSessionFactoryBean,只好試著改看看有沒有辦法可以讓我懶一點不要再去重訂hibernate.cfg.xml。


發現hibernate3-maven-plugin中有一個AnnotationComponentConfiguration,看來似是一個不錯的切入點,稍為改了一下還發現真能work!下面列的就是實做出來的東西。


1.pom.xml

改maven的plugin不用maven也太說不過去了吧,所以pom.xml是一定要的。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

 <modelVersion>4.0.0</modelVersion>

 <groupId>org.elliot.hibernate</groupId>
 <artifactId>maven-hibernate3-jdk15</artifactId>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Hibernate3 Implementation - JDK15</name>
 <packaging>jar</packaging>
 <dependencies>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-model</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-plugin-api</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-artifact</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-project</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.codehaus.mojo.hibernate3</groupId>
   <artifactId>maven-hibernate3-jdk15</artifactId>
   <version>2.2</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>3.3.2.GA</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.4.0.GA</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>ejb3-persistence</artifactId>
   <version>1.0.2.GA</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-annotations</artifactId>
   <version>3.4.0.GA</version>
  </dependency>
  <dependency>
   <groupId>jboss</groupId>
   <artifactId>jboss-common</artifactId>
   <version>4.0.2</version>
  </dependency>
  <dependency>
   <groupId>javassist</groupId>
   <artifactId>javassist</artifactId>
   <version>3.4.GA</version>
  </dependency>
  <!--可視情形改用其他版本的Spring-->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>org.springframework.orm</artifactId>
   <version>3.0.0.RC1</version>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>1.5</source>
     <target>1.5</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>
2. SpringComponentConfiguration.java

我暫用org.codehaus.mojo.hibernate3.configuration這package,怕AnnotationComponentConfiguration會不會有什麼method不能用,後來看code發現沒問題,也可以改成你喜歡的名稱

package org.codehaus.mojo.hibernate3.configuration;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.mojo.hibernate3.ExporterMojo;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;
import org.springframework.util.StringUtils;

public class SpringComponentConfiguration extends
  AnnotationComponentConfiguration {
 private static final Log logger = LogFactory
   .getLog(SpringComponentConfiguration.class);

 private ExporterMojo exporterMojo;
 private ApplicationContext applicationContext;

 public String getName() {
  return "springconfiguration";
 }

 public ExporterMojo getExporterMojo() {
  return exporterMojo;
 }

 public void setExporterMojo(ExporterMojo exporterMojo) {
  this.exporterMojo = exporterMojo;
 }

 @Override
 protected Configuration createConfiguration() {
  String sessionFactoryBean = getExporterMojo().getComponentProperty(
    "sessionFactoryBean", "sessionFactory");
  String appContextLocations = getExporterMojo().getComponentProperty(
    "appContextLocations", "classpath*:spring*.xml");
  logger.info("Initial info: [sessionFactoryBean]=" + sessionFactoryBean
    + ", [appContextLocations]=" + appContextLocations);

  String[] locations = StringUtils.delimitedListToStringArray(
    appContextLocations, ",");
  // Initial ApplicationContext from spring configuration xml files.
  this.applicationContext = new FileSystemXmlApplicationContext(locations);

  // Get AnnotationSessionFactoryBean from spring
  AnnotationSessionFactoryBean asfb = (AnnotationSessionFactoryBean) applicationContext
    .getBean("&" + sessionFactoryBean);

  DataSource dataSource = asfb.getDataSource();
  ThreadLocalHolder.setDataSource(dataSource);

  Configuration configuration = asfb.getConfiguration();
  configuration.setProperty(Environment.CONNECTION_PROVIDER,
    ThreadLocalConnectionProvider.class.getName());

  return configuration;
 }

 @Override
 protected void validateParameters() throws MojoExecutionException {
  // don't validate
 }

}
3.ThreadLocalConnectionProvider.java

因為Configuration不能直接設DataSource,所以只好放到ThreadLocal

package org.codehaus.mojo.hibernate3.configuration;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.connection.ConnectionProvider;

public class ThreadLocalConnectionProvider implements ConnectionProvider {
 private static final Log logger = LogFactory.getLog(ThreadLocalConnectionProvider.class);
 public ThreadLocalConnectionProvider() {
 }
 public void close() throws HibernateException {

 }

 public void closeConnection(Connection conn) throws SQLException {
  conn.close();
 }

 public void configure(Properties props) throws HibernateException {
  //Do nothing

 }

 public Connection getConnection() throws SQLException {
  DataSource dataSource = ThreadLocalHolder.getDataSource();
  if (null == dataSource) {
   logger.error("Please check ThreadLocalHolder.setDataSource has been invocked.");
   throw new SQLException("No usable DataSource.");
  }
  return dataSource.getConnection();
 }

 public boolean supportsAggressiveRelease() {
  // TODO Auto-generated method stub
  return true;
 }

}
4.ThreadLocalHolder.java

package org.codehaus.mojo.hibernate3.configuration;

import javax.sql.DataSource;

public abstract class ThreadLocalHolder {
 private static ThreadLocal<DataSource> dataSourceHolder = new ThreadLocal<DataSource>();

 public static DataSource getDataSource() {
  return dataSourceHolder.get();
 }

 public static void setDataSource(DataSource dataSource) {
  dataSourceHolder.set(dataSource);
 }
 
}
5.components.xml

這檔案放在src/main/resources/META-INF/plexus下,是maven plugin的設定檔

<component-set>
  <components>
 <component>
      <role>org.codehaus.mojo.hibernate3.configuration.ComponentConfiguration</role>
      <role-hint>springconfiguration</role-hint>
      <implementation>org.codehaus.mojo.hibernate3.configuration.SpringComponentConfiguration</implementation>
    </component>
  </components>
</component-set>
完成之後就用mvn install裝到repository裡以供使用,使用方式就只是改變hibernate3-maven-plugin中的dependency。

<build>
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>hibernate3-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
     <componentProperties>
      <jdk5>true</jdk5>
      <implementation>springconfiguration</implementation>
      <appContextLocations>classpath*:applicationContext.xml</appContextLocations>
      <sessionFactoryBean>sessionFactory</sessionFactoryBean>
     </componentProperties>
    </configuration>
    <dependencies>
     <dependency>
      <groupId>org.elliot.hibernate</groupId>
      <artifactId>maven-hibernate3-jdk15</artifactId>
      <version>1.0-SNAPSHOT</version>
     </dependency>
     <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-ehcache</artifactId>
      <version>3.3.2.GA</version>
     </dependency>
     <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.4.0.GA</version>
     </dependency>
     <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.2.121</version>
     </dependency>
    </dependencies>
   </plugin>
  </plugins>
 </build>
加入configuration中列出的設定,appContextLocations是專案中spring設定檔的檔案位置,sessionFactoryBean則是spring中你定義的AnnotationSessionFactoryBean名稱。

再來就可以用mvn hibernate3:hbm2ddl 之類的goals來測試囉。

08 2月 2007

Hibernate Annotations - One-to-One mappedBy的影響

One-to-One mappedBy的影響 Hibernate Annotations的說明
The association may be bidirectional. In a bidirectional relationship, one of the sides (and only one) has to be
the owner: the owner is responsible for the association column(s) update. To declare a side as not responsible
for the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association on
the owner side.
在Hibernate中雙向的Relationship的維護上,必定有一方為Owner來記錄此Relationship. 而mappedBy就是為了宣告其本身不負責記錄Relationship,交由另一方記錄所使用的annotation attribute. Bidirectional 的 One-to-One通常還是有主從的概念,以下列Person為主,Passport為從來看 (1)將mappedBy設在Person(從)上
public class Person implements Serializable {
 @Id
 @GeneratedValue(generator = "uuid-gen")
 @GenericGenerator(name = "uuid-gen", strategy = "uuid")
 @Column(length = 32)
 private String id;
 
 @OneToOne(cascade= CascadeType.ALL)
 private Passport passport;
}
public class Passport implements Serializable {
 @Id
 @GeneratedValue(generator = "uuid-gen")
 @GenericGenerator(name = "uuid-gen", strategy = "uuid")
 @Column(length = 32)
 private String identificationCode;
 
 @OneToOne(mappedBy="passport")
 private Person owner;
}
Create Table SQL
create table Passport (identificationCode varchar(32) not null, primary key (identificationCode))
create table Person (id varchar(32) not null, version integer, name varchar(50), firstName varchar(50), lastName varchar(50), mutable bit, birthday date, age integer, modifyDate timestamp, description longvarchar, passport_identificationCode varchar(32), primary key (id))
alter table Person add constraint FK8E488775EB061869 foreign key (passport_identificationCode) references Passport
Hibernate SQL Execution
Hibernate: insert into Person (version, name, firstName, lastName, mutable, birthday, age, modifyDate, description, passport_identificationCode, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select this_.id as id0_1_, this_.version as version0_1_, this_.name as name0_1_, this_.firstName as firstName0_1_, this_.lastName as lastName0_1_, this_.mutable as mutable0_1_, this_.birthday as birthday0_1_, this_.age as age0_1_, this_.modifyDate as modifyDate0_1_, this_.description as descrip10_0_1_, this_.passport_identificationCode as passport11_0_1_, passport2_.identificationCode as identifi1_1_0_ from Person this_ left outer join Passport passport2_ on this_.passport_identificationCode=passport2_.identificationCode
Hibernate: insert into Passport (identificationCode) values (?)
Hibernate: update Person set version=?, name=?, firstName=?, lastName=?, mutable=?, birthday=?, age=?, modifyDate=?, description=?, passport_identificationCode=? where id=? and version=?
Hibernate會將passport_identificationCode建在Person的Table中,而在Select時,會一併帶出Passport的資料 (2)將mappedBy設在Passport(主)上
public class Person implements Serializable {
 @Id
 @GeneratedValue(generator = "uuid-gen")
 @GenericGenerator(name = "uuid-gen", strategy = "uuid")
 @Column(length = 32)
 private String id;
 
 @OneToOne(mappedBy="owner",cascade= CascadeType.ALL)
 private Passport passport;
}
public class Passport implements Serializable {
 @Id
 @GeneratedValue(generator = "uuid-gen")
 @GenericGenerator(name = "uuid-gen", strategy = "uuid")
 @Column(length = 32)
 private String identificationCode;
 
 @OneToOne
 private Person owner;
}
Create Table SQL
create table Passport (identificationCode varchar(32) not null, owner_id varchar(32), primary key (identificationCode))
create table Person (id varchar(32) not null, version integer, name varchar(50), firstName varchar(50), lastName varchar(50), mutable bit, birthday date, age integer, modifyDate timestamp, description longvarchar, primary key (id))
alter table Passport add constraint FK4C60F032538A0EEB foreign key (owner_id) references Person
Hibernate SQL Execution
Hibernate: insert into Person (version, name, firstName, lastName, mutable, birthday, age, modifyDate, description, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select this_.id as id0_1_, this_.version as version0_1_, this_.name as name0_1_, this_.firstName as firstName0_1_, this_.lastName as lastName0_1_, this_.mutable as mutable0_1_, this_.birthday as birthday0_1_, this_.age as age0_1_, this_.modifyDate as modifyDate0_1_, this_.description as descrip10_0_1_, passport2_.identificationCode as identifi1_1_0_, passport2_.owner_id as owner2_1_0_ from Person this_ left outer join Passport passport2_ on this_.id=passport2_.owner_id
Hibernate: insert into Passport (owner_id, identificationCode) values (?, ?)
Hibernate: update Person set version=?, name=?, firstName=?, lastName=?, mutable=?, birthday=?, age=?, modifyDate=?, description=? where id=? and version=?
Hibernate會將owner_id建在Passport的Table中,而在Select時,不會一併帶出Passport的資料 可能的話,應該還是將mappedBy設在(主)的那一方上,讓(從)的一方記錄relationship的欄位. 否則的話就如同Component,不如就整在同一Table裡,還可以省去join的時間.

29 1月 2007

Use xdoclet2 hibernate plugin in Maven2

(1) Add pluginRepositories element to your pom.xml
<project>
 <pluginRepositories>
  <pluginRepository>
   <id>codehaus-plugins</id>
   <url>http://dist.codehaus.org/</url>
   <layout>legacy</layout>
   <snapshots>
    <enabled>true</enabled>
   </snapshots>
   <releases>
    <enabled>true</enabled>
   </releases>
  </pluginRepository>
 </pluginRepositories>
</project>
(2) Add xdoclet2 plugin to pom.xml
<plugins>
 <plugin>
  <groupId>xdoclet</groupId>
  <artifactId>maven2-xdoclet2-plugin</artifactId>
  <executions>
   <execution>
    <id>xdoclet</id>
    <phase>generate-sources</phase>
    <goals>
     <goal>xdoclet</goal>
    </goals>
   </execution>
  </executions>
  <dependencies>
   <dependency>
    <groupId>xdoclet-plugins</groupId>
    <artifactId>
     xdoclet-plugin-hibernate
    </artifactId>
    <version>1.0.4-SNAPSHOT</version>
   </dependency>
  </dependencies>
  <configuration>
   <configs>
    <config>
     <components>
      <component>
       <classname>
        org.xdoclet.plugin.hibernate.HibernateMappingPlugin
       </classname>
      </component>
     </components>
     <params>
      <version>3.0</version>
     </params>
    </config>
   </configs>
  </configuration>
 </plugin>
</plugins>
(3) Run mvn compile

Technorati Tags: , ,