alt="Unit-Testing Hibernate With HSQLDB">





April 2005


Discussion






The Motivation


I've used lots of methods to transform data between databases and object code.
From hand-coded SQL to JDO to EJB. I've never found a method I liked particularly
well. This distaste has become especially acute since adopting test-driven
development (TDD) as a guiding philosophy.


Unit-testing should have as few barriers as possible. For relational databases
those barriers range from external dependencies (is the database running?)
to speed to keeping the relational schema synchronized with your object model.
For these reasons it is vital to keep database access code away from the core
object model and to test as much as possible without touching a real database.


This has often led me to one of two patterns. The first is externalizing all
data access to domain objects and their relationships to separate classes or
interfaces. These are typically data store objects that can retrieve, edit,
delete and add domain entities. This is the easiest to mock-out for unit-testing,
but tends to leave your domain model objects as data-only objects with little
or no related behavior. Ideally access to child records would be directly from
the parent object rather than handing the parent object to some third-party
class to determine the children.


The other method has been to have the domain objects have access to an interface
into the data-mapping layer a la Martin Fowlers Data Mapper pattern.
This has the advantage of pushing object relationships inside the domain model
where the object-relational interface can be expressed once. Classes that use
the domain model are unaware of the persistence mechanism because it is internalized
into the domain model itself. This keeps your code focused on the business
problem you are trying to solve and less about the object-relational mapping
mechanism.


My current project involves crunching a number of baseball statistics and
running simulations with the data. Since the data was already in a relational
database it was a chance for me to explore the Hibernate object-relational
mapping system. I have been very impressed with Hibernate, but I ran into the
problem was trying to insert a layer of indirection while using Hibernate as
my data mapper for unit-testing. The extra layer was so flimsy that it felt
embarrassing to write it. The real deployed version was simply a pass-through
to a Hibernate-specific implementation. Even worse, the mock versions had more
complexity in them than the real "production" version simply because
they didn't have some of the basic object storage and mapping that came with
Hibernate.


I also had enough complex Hibernate query usage that I wanted to unit-test
this significat portion of the application. However, testing against a live database
is a bad idea, because it almost invariably introduces a maintenance nightmare.
In addition, since tests are best when they are independent from each other,
using the same obvious primary keys in test fixture data means you have to
create code to clean the database before each test case, which is a real problem
when lots of relationships are involved


By using HSQLDB and Hibernate's powerful schema-generation tool I was able
to unit-test the mapping layer of the application and find numerous bugs in
my object queries I would not have found as easily by manual testing. With
the techniques outlines below I was able unit-test my entire application during
development with no compromises in test coverage.


Setting up HSQLDB


I used version 1.7.3.0 of HSQLDB. To use an in-memory version of the database
you need to invoke the static loader for the org.hsqldb.jdbcDriver. Then when
you get a JDBC connection you use JDBC url such as jdbc:hsqldb:mem:yourdb where
'yourdb' is the name of the in-memory database you want to use.


Since I'm using Hibernate (3.0 beta 4), I hardly ever need to touch real-live
JDBC objects. Instead I can let Hibernate do the heavy lifting for me--including
automatically creating the database schema from my Hibernate mapping files.
Since Hibernate creates its own connection pool it will automatically load
the HSQLDB JDBC driver based on the configuration code lives in a class called
TestSchema. Below is the static initializer for the class.



public class TestSchema {

static {
Configuration config = new Configuration().
setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect").
setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver").
setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:baseball").
setProperty("hibernate.connection.username", "sa").
setProperty("hibernate.connection.password", "").
setProperty("hibernate.connection.pool_size", "1").
setProperty("hibernate.connection.autocommit", "true").
setProperty("hibernate.cache.provider_class", "org.hibernate.cache.HashtableCacheProvider").
setProperty("hibernate.hbm2ddl.auto", "create-drop").
setProperty("hibernate.show_sql", "true").
addClass(Player.class).
addClass(BattingStint.class).
addClass(FieldingStint.class).
addClass(PitchingStint.class);

HibernateUtil.setSessionFactory(config.buildSessionFactory());
}

Hibernate provides a number of different ways to configure the framework,
including programmatic configuration. The code above sets up the connection
pool. Note that the user name 'sa' is required to use HSQLDB's in-memory
database. Also be sure to specify a blank as the password. To enable Hibernate's
automatic schema generation set the hibernate.hbm2ddl.auto property to 'create-drop'.


Testing In Practice


My project is crunching a bunch of baseball statistics so I add the four classes
that I'm mapping ( Player, PitchingStint, BattingStint and FieldingStint).
Finally I create a Hibernate SessionFactory and insert it into the HibernateUtil
class which simply provides a single access method for my entire application
for Hibernate sessions. The code for the HibernateUtil is below:



import org.hibernate.*;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

private static SessionFactory factory;

public static synchronized Session getSession() {
if (factory == null) {
factory = new Configuration().configure().buildSessionFactory();
}
return factory.openSession();
}

public static void setSessionFactory(SessionFactory factory) {
HibernateUtil.factory = factory;
}
}

Since all of my code (production code as well as unit-tests) get their Hibernate
sessions from the HibernateUtil I can configure it in one place. For unit-tests
the first bit of code to access the TestSchema class will invoke the static
initializer which will setup Hibernate and inject the test SessionFactory into
the HibernateUtil. For production code the SessionFactory will be initialized
lazily using the standard hibernate.cfg.xml configuration mechanism.


So what does this look like in the unit-tests? Below is a snippet of a test
that checks the the logic for determining what positions a player is eligible
to play at for a fantasy baseball league:



public void testGetEligiblePositions() throws Exception {
Player player = new Player("playerId");
TestSchema.addPlayer(player);

FieldingStint stint1 = new FieldingStint("playerId", 2004, "SEA", Position.CATCHER);
stint1.setGames(20);
TestSchema.addFieldingStint(stint1);

Set positions = player.getEligiblePositions(2004);
assertEquals(1, positions.size());
assertTrue(positions.contains(Position.CATCHER));
}

I first create a new Player instance and add it to the TestSchema via the
addPlayer() method. This step must occur first because the FieldingStint
class has a foreign-key relationship to the Player class. If I didn't add this
instance first I would get a foreign-key constraint violation when I try to
add the FieldingStint. Once the test-fixture is in place I can test the getEligiblePositions()
method to see that it retrieves the correct data. Below is the code for the
addPlayer() method in the TestSchema. You will notice that Hibernate is used
instead of bare-metal JDBC code:


}



public static void addPlayer(Player player) {
if (player.getPlayerId() == null) {
throw new IllegalArgumentException("No primary key specified");
}

Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
try {
session.save(player, player.getPlayerId());
transaction.commit();
}
finally {
session.close();
}
}

One of the most important things in unit-testing is to keep your test-cases
isolated. Since this method still involves a database, you need a way to clean
your database prior to each test case. I have four tables in my schema so I
wrote a reset() method on the TestSchema that removes all rows from the tables
using JDBC. Note because HSQLDB knows about foreign keys, the order in which
the tables are deleted is important. Here is the code:



public static void reset() throws SchemaException {
Session session = HibernateUtil.getSession();
try {
Connection connection = session.connection();
try {
Statement statement = connection.createStatement();
try {
statement.executeUpdate("delete from Batting");
statement.executeUpdate("delete from Fielding");
statement.executeUpdate("delete from Pitching");
statement.executeUpdate("delete from Player");
connection.commit();
}
finally {
statement.close();
}
}
catch (HibernateException e) {
connection.rollback();
throw new SchemaException(e);
}
catch (SQLException e) {
connection.rollback();
throw new SchemaException(e);
}
}
catch (SQLException e) {
throw new SchemaException(e);
}
finally {
session.close();
}
}

When bulk deletes become finalized in Hibernate 3.0 we should able to remove
this last bit of direct JDBC from our application. Until then we have to get
a Connection and issue direct SQL to the database.


Be sure not to close your Connection, closing the Session is sufficient for
resource cleanup. Out of habits developed from writing lots of hand-crafted
JDBC code, the first version closed the JDBC Connection. Since I configured
Hibernate to create a connection pool with only one Connection I completely
torpedoed any tests after the first one.Be sure to watch out for this!


Since you can never be sure what state the database may be in when your test
class is running (imagine running all of your test cases), you should include
database cleanup in your setUp() methods like so:



public void setUp() throws Exception {
TestSchema.reset();
}

Conclusion


Being able to test against a real-live RDBMS without all of the hassles of
trying to run tests against your deployed database is essential, even when
working with sophisticated O/R mappers like Hibernate. The example I showed
here is not exclusive to Hibernate and could probably be made to work with
JDO or TopLink, though Hibernate makes this kind of testing particularly easy
since it has a built-in schema generation tool. With a setup like the one described
above you don't ever have to leave the comfort of your IDE and still have extensive
test coverage over your code.


Posted by 아름프로


EJB3 and Hibernate3 Annotations






Hibernate Annotations


EJB3 and Hibernate3 mapping metadata with JDK 5.0 Annotations




Version: 3.0 Preview beta1, 07 04 2005






Please note that this documentation is based on a preview release
of the Hibernate Annotations and implements the Early Draft Release 2 of
EJB 3.0/JSR-220 persistence annotations. However, we expect that this work is already
very close to the final concepts in the new specification. Our goal is to provide
a complete set of ORM annotations, including EJB3 standard annotations as well as
Hibernate3 extensions for cases not covered by the specification. See the href="#gen37">Compliance and Limitations section for more
information.



1. Introduction



Hibernate, like all other object/relational mapping tools, requires metadata that
governs the transformation of data from one representation to the other (and vice
versa). In Hibernate 2.x, mapping metadata is most of the time declared in XML text
files. Another option is XDoclet, utilizing Javadoc source code annotations and a
preprocessor at compile time. The same kind of annotation support is now available in
the standard JDK, although more powerful and better supported by tools. IntelliJ IDEA,
and Eclipse for example, supports auto-completion and syntax highlighting of JDK 5.0
annotations.
The new revision of the EJB specification (JSR-220) uses JDK 5.0 annotations as the
primary metadata mechanism. Hibernate3 implements the EntityManager of JSR-220
(the persistence API). The Hibernate Annotations package contains the EJB3 and Hibernate3
extension annotations and their internal binding to the Hibernate core.



2. Requirements




  • Download and unpack hibernate-annotations-3.0beta1 from the Hibernate website

  • Download and unpack hibernate-3.0 from the Hibernate website



  • Make sure you have JDK 5.0 installed.
    You can of course continue using XDoclet and get some of the benefits of
    annotation-based metadata with older JDK versions. Note that this document only
    describes JDK 5.0 annotations and you have to refer to the XDoclet documentation for
    more information.



    3. Setup and configuration



    First, set up your classpath (after you have created a new project in your favorite IDE):



  • Copy all Hibernate3 core and required 3rd party library files (see lib/README.txt in Hibernate).

  • Copy hibernate-annotations.jar and lib/ejb-3.0-edr2.jar from the
    Hibernate Annotations distribution to your classpath as well.

    We also recommend a small wrapper class to startup Hibernate in a static initializer block,
    known as HibernateUtil. You might have seen this class in various forms in other
    areas of the Hibernate documentation. For Annotation support you have to enhance this
    helper class as follows:





    package hello;

    import org.hibernate.*;
    import org.hibernate.cfg.*;
    import test.*;
    import test.animals.Dog;

    public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
    try {

    sessionFactory = new AnnotationConfiguration()

    .addPackage("test")
    .addAnnotatedClass(Flight.class)
    .addAnnotatedClass(Sky.class)
    .addAnnotatedClass(Person.class)
    .addAnnotatedClass(Dog.class)

    .buildSessionFactory();
    } catch (Throwable ex) {
    // Log exception!
    throw new ExceptionInInitializerError(ex);
    }
    }

    public static Session getSession()
    throws HibernateException {
    return sessionFactory.openSession();
    }
    }



    Interesting here is the use of AnnotationConfiguration and the declaration
    of a package and persistent classes.


    Alternatively, you can declare your annotated packages and classes in your usual
    XML configuration file (usually hibernate.cfg.xml). Here is the equivalent of the
    above declaration




    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>
    <session-factory>
    <mapping resource="test/Boat.hbm.xml"/>
    <mapping package="test"/>
    <mapping class="test.Flight"/>
    <mapping class="test.Sky"/>
    <mapping class="test.Person"/>
    <mapping class="test.animals.Dog"/>

    </session-factory>
    </hibernate-configuration>


    This kind of declaration is certainly a preferred way. Note that you can mix the the hbm.xml use
    and the new annotation one.



    There is no other difference in the way you use Hibernate APIs with Annotations, expect for
    this change in the startup routine or in the configuration file. You can use your favorite
    configuration method for other properties (hibernate.properties, hibernate.cfg.xml, programmatic
    APIs, etc). You can even mix annotated persistent classes and classic hbm.cfg.xml
    declarations with the same SessionFactory. You can however not declare a class several
    times (whether annotated or through hbm.xml). For annotated classes, the classes have to be added
    before their subclasses (this limitation will likely be removed soon), e.g.:





    cfg.addAnnotatedClass(Animal.class);
    cfg.addAnnotatedClass(Dog.class); // OK

    cfg.addAnnotatedClass(Dog.class);
    cfg.addAnnotatedClass(Animal.class); // AnnotationException!



    4. Mapping with EJB3 Annotations



    EJB3 entity beans are plain POJOs. Actually they represent the exact same concept as the
    Hibernate entities. Their mappings are defined through the JDK 5.0
    annotation feature (an XML descriptor syntax hasn't been finalized in the specification).
    Annotations come in two categories, the logical mapping annotations (allowing you to
    describe the object model, the class associations, etc.) and the physical mapping
    annotations (decribing the physical schema, tables, columns, indexes, etc). We will
    mix annotations from both categories in the following code examples.


    Annotations are in the javax.persistence.* package. IntelliJ is know to work well on it.



    4.1 Declaring an entity bean



    Every bound persistent POJO class is an entity bean and is declared using the
    @Entity annotation (at the class level):





    @Entity
    public class Flight implements Serializable {
    Long id;

    @Id
    public Long getId() { return id; }

    public setId(Long id) { this.id = id; }
    }



    For a full example, see Flight.java
    of the test suite.



    @Entity declares the class as an entity bean (i.e. a persistent POJO class),
    @Id declares the identifier property of this entity bean. The other mapping
    declarations are implicit. This configuration by exception concept is central to the
    new EJB3 specification and a major improvement. The class Flight is mapped to
    the Flight table, using the column id as its primary key column.



    The @Entity annotation allows you to define whether an entity bean should be
    accessed through its getters/setters methods (default) or wheher the entity manager should
    access the fields of the object directly:




    @Entity(access = AccessType.PROPERTY)
    or
    @Entity(access = AccessType.FIELD)



    The EJB3 spec requires that you declare annotations on the artefact
    that will be used, i.e. the getter method if you use PROPERTY access, the
    field if you use FIELD access.



    4.1.1 Defining the table



    @Table is set at the class level; it allows you to define the table,
    catalog, and schema names for your entity bean mapping. If no @Table is
    defined the default values are used: the unqualified class name of the entity.





    @Entity(access=AccessType.FIELD)
    @Table(name="tbl_sky")
    public class Sky implements Serializable {
    ...



    You can also define unique constraints to the table using the @UniqueConstraint annotation
    in conjunction with @Table (for a unique constraint bound to a sincle column, refer to @column).
    For a full example, see Sky.java
    of the test suite.



    4.1.2 Versioning for optimistic locking



    You can add optimistic locking capability to an entity bean using the
    @Version annotation:





    @Entity()
    public class Flight implements Serializable {
    ...
    @Version
    @Column(name="OPTLOCK")

    public Integer getVersion() { ... }
    }



    The version property will be mapped to the OPTLOCK column,
    and the entity manager will use it to detect conflicting updates (preventing lost
    updates you might otherwise see with the last-commit-wins strategy).



    4.1.3 Entity specific extensions


    To empower the EJB3 capabilities, hibernate provides specific annotations that
    match hibernate features. The org.hibernate.annotations package contains
    all these annotations extensions. Pleaser refer to the
    Hibernate Annotations API
    for a complete list.


    @org.hibernate.annotations.Entity extends @javax.persistence.Entity :


    • mutable: whether this entity is mutable or not

    • dynamicInsert: allow dynamic SQL for inserts

    • dynamicUpdate: allow dynamic SQL for updates

    • selectBeforeUpdate: Specifies that Hibernate should never perform an
      SQL UPDATE unless it is certain that an object is actually modified.

    • polymorphism: whether the entity polymorphism is of
      PolymorphismType.IMPLICIT (default) or PolymorphismType.IMPLICIT

    • persister: allow the overriding of the default persister implementation

    • optimisticLock: optimistic locking strategy (VERSION, NONE, DIRTY or ALL)


    @org.hibernate.annotations.BatchSize allows you to define the batch size when fetching
    instances of this entity ( eg. @BatchSize(size=4) )

    @org.hibernate.annotations.Proxy defines the lazyness attributes of the entity.
    lazy (default to true) define whether the class is lazy or not. proxyClassName
    is the interface to use for the lazy initializing proxies.

    @org.hibernate.annotations.Where defines an optional SQL WHERE clause used when
    instances of this class is retrieved.

    @org.hibernate.annotations.Check defines an optional check constraints defined
    at the class level.

    @org.hibernate.annotations.Cache defines the caching strategy and region of the class.
    This annotation is defined at the root entity (not the subentities).



    @org.hibernate.annotations.Filter or @Filters define filter(s) to an entity.
    A filter is defined by a name() and by a SQL condition()
    (with potential parameters).

    @org.hibernate.annotations.FilterDef or @FilterDefs define filter
    definition(s) used by filter(s) using the same name. A filter definition has a name()
    and an array of parameters(). A @ParamDef has a name and a type. A @FilterDef(s)
    can be defined at the class or package level.




    @Entity
    @BatchSize(size=5)
    @org.hibernate.annotations.Entity(
    selectBeforeUpdate = true,
    dynamicInsert = true, dynamicUpdate = true,
    optimisticLock = OptimisticLockType.ALL,
    polymorphism = PolymorphismType.EXPLICIT)
    @Where(clause="1=1")
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    @FilterDef(name="minLength", parameters={ @ParamDef( name="minLength", type="integer" ) } )
    @Filters( {
    @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
    @Filter(name="minLength", condition=":minLength <= length")
    } )
    public class Forest {



    Please refer to the test suite's
    entity package for more
    working examples.



    4.2 Mapping simple properties



    4.2.1 Declaring basic property mappings



    By default, every property of an entity bean is mapped persistent, unless you
    annotate it as @Transient. Not having an annotation for your property is
    equivalent to an appropriate basic annotation (see 4.2.4 for more details).
    The @Basic annotation allows
    you to declare the fetching strategy for a property:





    @Transient
    String getLengthInMeter() { ... }

    String getName() {... } // persistent property

    @Basic
    int getLength() { ... } // persistent property

    @Basic(fetch = FetchType.LAZY)
    String getDetailedComment() { ... }



    The lengthInMeter property is mapped transient and will be ignored by the
    entity manager. The name and the length properties are mapped persistent
    and eagerly fetched (the default for simple properties). The detailedComment
    property value will be lazily fetched from the database once a lazy property of the entity
    is accessed for the first time. The compiled code of your class has to be instrumented for
    this feature, please refer to the Hibernate reference documentation. Usually you don't need
    lazy simple property fetching (not to be confused with lazy association fetching) and can avoid
    any build-time bytecode instrumentation with Hibernate. Hibernate will silently disable lazy
    property fetching if you don't instrument the compiled class.


    @Serialized indicates that the property should be persisted as a serialized
    bytestream.


    @Lob indicates that the property should be persisted in a Blob or a Clob depending
    the of LobType. java.sql.Clob, Character[], char[] and String
    can be persisted in a Clob. java.sql.Blob, Byte[] and byte[]
    can be persisted in a Blob.



    	@Serialized
    public Country getCountry() {
    ...
    }

    @Lob(type=LobType.CLOB)
    public String getFullText() {
    return fullText;
    }

    @Lob(type = LobType.BLOB)
    public byte[] getFullCode() {
    return fullCode;
    }



    4.2.2 Declaring column attributes



    The columns use for a property mapping can be defined using the @Column annotation.
    Use it to override default values (see the EJB3 specification for more information on the
    defaults). You can use this annotation at the property level for properties that are, not annotated
    at all, annotated with @Basic, @Version, @Serialized
    and @Lob:





    @Entity()
    public class Flight implements Serializable {
    ...
    @Column(updatable = false, name = "flight_name", nullable = false, length=50)
    public String getName() { ... }



    The name property is mapped to the flight_name column, which is
    not nullable, has a length of 50 and is not updatable (making the property immutable).
    For more attributes of @Column please refer to the EJB3 spec.



    4.2.3 Embedded objects (components)



    It is possible to declare an embedded component inside an entity and even override its
    column mapping. Component classes (also known as user-defined value types) have to be
    annotated at the class level with the @Embeddable annotation. It is
    possible to override the column mapping of an embedded object for a particular entity
    using the @Embedded annotation in the associated property:





    @Entity(access = AccessType.FIELD)
    public class Person implements Serializable {

    // Persistent component using defaults
    Address homeAddress;

    @Embedded({
    @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
    @AttributeOverride(name="name", column = @Column(name="bornCountryName") )
    })

    Country bornIn;
    ...
    }







    @Embeddable(access = AccessType.FIELD)
    public class Address implements Serializable {
    String city;
    Country nationality;
    }







    @Embeddable
    public class Country implements Serializable {
    private String iso2;
    private String name;

    public String getIso2() { return iso2; }
    public void setIso2(String iso2) { this.iso2 = iso2; }

    @Column(name="countryName")
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    ...
    }



    The Person entity bean has two component properties, homeAddress and
    bornIn. Note that the homeAddress property has not been annotated.
    Hibernate will guess that it is a persistent component after looking for the
    @Embeddable in the Address class. We also override the mapping of
    a column name (to bornCountryName) with the @Embedded and
    @AttributeOverride annotations for each mapped attribute of Country.
    As you can see, Country is also nested component of Address, again using
    auto-detection by Hibernate and EJB3 defaults. Overriding columns of dependent objects
    of dependent objects is currently not supported in the EJB3 spec (user feedbacks are welcome).



    4.2.4 Non annotated property


    • If the property is of a single type, it is mapped as @Basic

    • Otherwise, if the type of the property is annotated as @Embeddable

    • Otherwise, if the type of the property is Serializable, it is mapped as
      @Serialized

    • Otherwise, if the type of the property is java.sql.Clob or
      java.sql.Blob, it is mapped as @Lob with the appropriate
      LobType



    4.2.5 Property specific extensions


    @org.hibernate.annotations.Type overrides the default hibernate type used: this
    is generally not necessary since the type is correctly inferred by Hibernate.
    Please refer to the Hibernate reference guide for more informations on the Hibernate types.

    @org.hibernate.annotations.TypeDef and @org.hibernate.annotations.TypeDefs
    allows you to define type defintions. These annotations are placed at the class or package level.
    Note that these definitions will be global for the session factory (even at the class level)
    and that type definition has to be defined before any usage.




    @TypeDefs(
    {
    @TypeDef(
    name="caster",
    typeClass = CasterStringType.class,
    parameters = {
    @Parameter(name="cast", value="lower")
    }
    )
    }
    )
    package org.hibernate.test.annotations.entity;

    ...
    public class Forest {
    @Type(type="caster")
    public String getSmallText() {
    ...
    }



    4.3 Mapping identifier properties



    The @Id annotation lets you define which property is the identifier of your
    entity bean. It also allows you to define the identifier generation strategy:




    • AUTO - either identity column or sequence depending the underlying DB
    • TABLE - table holding the id
    • IDENTITY - identity column
    • SEQUENCE - sequence
    • NONE - the application has the responsability to set the id


    The following example shows a sequence generator using the SEQ_STORE
    configuration (see below):





    @Id(generate=GeneratorType.SEQUENCE, generator="SEQ_STORE")
    public Integer getId() { ... }



    The next example uses the identity generator:





    @Id(generate=GeneratorType.IDENTITY)
    public Integer getId() { ... }



    The AUTO generator is the preferred type for portable applications (accross several dB vendors).
    The identifier generation configuration can be shared for several @Id mappings
    with the generator attribute. There are several configurations available through
    @SequenceGenerator, @TableGenerator and @GeneratorTable. The scope
    of a generator can be the application or the class. Class-defined generators are not visible outside
    the class and can override application level generators. Application level generators are
    defined at package level
    (see package-info.java):






    @javax.persistence.GeneratedIdTable(
    name="GEN_TABLE",
    table = @Table(name="GENERATOR_TABLE"),
    pkColumnName = "key",
    valueColumnName = "hi"
    )
    @javax.persistence.TableGenerator(
    name="EMP_GEN",
    tableName="GEN_TABLE",
    pkColumnValue="EMP",
    allocationSize=20
    )
    @javax.persistence.SequenceGenerator(
    name="SEQ_GEN",
    sequenceName="my_sequence"
    )

    package org.hibernate.test.metadata;



    If cfg.addPackage("org.hibernate.test.metadata") is used to initialize the
    Hibernate config, EMP_GEN and SEQ_GEN are application level generators.
    EMP_GEN defines a table based id generator using the hilo algorithm with a
    max_lo of 20 and keeping the hi value in a row of a table defined by the GEN_TABLE
    @GeneratorTable. The row has EMP as a primary key value. The table (described by
    the @GeneratorTable) is GENERATOR_TABLE, had the column key
    (which hold the primary key) and the column hi (which hold the next hi value used).


    SEQ_GEN defines a sequence generator using a sequence
    named my_sequence. Note that this version of Hibernate Annotations does not
    handle initialValue and allocationSize parameters in the SequenceGenerator.

    The next example shows the definition of a sequence generator in a class scope:





    @Entity
    @javax.persistence.SequenceGenerator(
    name="SEQ_STORE",
    sequenceName="my_sequence"
    )

    public class Store implements Serializable {
    private Long id;

    @Id(generate=GeneratorType.SEQUENCE, generator="SEQ_STORE")
    public Long getId() { return id; }
    }



    This class will use a sequence named my_sequence and the SEQ_STORE
    generator is not visible in other classes. Note that you can Have a look at the Hibernate Annotations
    tests in the org.hibernate.test.metadata.id package for more examples.



    You can define a composite primary key through several syntaxes:

  • annotate the component property as @Id and make the component class @Embeddable

  • annotate the component property as @EmbeddedId

  • annotate the class as @IdClass (this mapping is currently not supported by the
    implementation).

    Have a look at Child.java and
    Parent.java for some
    examples.



    4.3 Mapping inheritance



    EJB3 supports the three types of inheritance:



    • Table per Class Strategy: the <class> element in Hibernate
    • Single Table per Class Hierarchy Strategy: the <subclass> element in Hibernate
    • Joined Subclass Strategy: the <joined-subclass> element in Hibernate


    The chosen strategy is declared at the class level using the @Inheritance annotation.

    4.3.1 Table per class



    This strategy has many drawbacks (esp. with polymorphic queries and associations)
    explained in the EJB3 spec,the Hibernate reference documentation, Hibernate in Action,
    and many other places. It is commonly used for the top level of an inheritance
    hierarchy:





    @Entity()
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public class Flight implements Serializable {



    You have to declare inheritance on the leave classes (note: this is an
    interpretation of the spec and is subject to change).



    4.3.2 Single table per class hierarchy



    All properties of all super- and subclasses are mapped to the same table, instances
    are distinquished with a special discriminator column:





    @Entity()
    @Inheritance(
    strategy=InheritanceType.SINGLE_TABLE,
    discriminatorType=DiscriminatorType.STRING,
    discriminatorValue="Plane"
    )
    @DiscriminatorColumn(name="planetype")

    public class Plane { ... }

    @Entity()
    @Inheritance(
    discriminatorValue="A320"
    )

    public class A320 extends Plane { ... }



    Plane is the superclass, it defines the inheritance strategy
    InheritanceType.SINGLE_TABLE, the discriminator type DiscriminatorType.STRING,
    and the discriminator value used for planes, "Plane". It also defines the
    discriminator column through the @DiscriminatorColumn(name="planetype"). Most of
    these atttributes have sensible default values. The default name of the discriminator column
    is TYPE, and (for Hibernate) the default discriminator value is the fully qualified
    class name. A320 is a subclass; you only have to define discriminatorValue
    if you won't accept the default. The strategy and the discriminator type is implicit.



    4.3.2 Joined subclasses



    This strategy has to be declared on all the subclasses. The @InheritanceJoinColumn
    and @InheritanceJoinColumns annotations define the primary key(s) of the joined
    subclass table. Note that the current implementation does not support implicit
    @InheritanceJoinColumns:





    @Entity()
    @Inheritance(strategy=InheritanceType.JOINED )
    public class Boat implements Serializable { ... }

    @Entity()
    @Inheritance(strategy=InheritanceType.JOINED)
    public class Ferry extends Boat { ... }

    @Entity(access=AccessType.FIELD)
    @Inheritance(strategy=InheritanceType.JOINED)
    @InheritanceJoinColumn(name="BOAT_ID")

    public class AmericaCupClass extends Boat { ... }



    Every entity bean declares the JOINED strategy, the Ferry
    table is joined with the Boat table using the same primary key names. The
    AmericaCupClass table is joined with Boat using the join
    condition Boat.id = AmericaCupClass.BOAT_ID.



    4.4 Mapping entity bean associations



    4.4.1 One-to-one



    You can associate entity beans with a one-to-one relationship using @OneToOne.
    There are two cases for one-to-one associations: either the associated entities share the
    same primary keys values or a foreign key is held by one of the entities (note that this FK
    column in the database should be constrained unique to simulate one-to-one multiplicity).



    First, we map a real one-to-one association using shared primary keys:





    @Entity
    public class Body {
    @Id
    public Long getId() { return id; }

    @OneToOne(cascade = CascadeType.ALL, usePKasFK=true)
    public Heart getHeart() {
    return heart;
    }
    ...
    }







    @Entity
    public class Heart {
    @Id(generate = GeneratorType.NONE)
    public Long getId() { ...}
    }



    Unfortunatly, in the current release of Hibernate Annotations, the Heart
    identifier value has to be set manually by the application developer. Support for a
    Hibernate-style "foreign" identifier generator will be added soon.



    In the following example, the associated entities are linked through a foreign key
    column :





    @Entity
    public class Customer implements Serializable {
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="passport_fk")
    public Passport getPassport() {
    ...
    }

    @Entity
    public class Passport implements Serializable {
    @OneToOne(mappedBy = "passport")
    public Customer getOwner() {
    ...
    }



    A Customer is linked to a Passport, with a foreign key column named
    passport_fk in the Customer table. The join column is declared with the
    @JoinColumn annotation which looks like the @Column annotation. It has
    one more parameter named referencedColumnName. This parameter declares the column
    in the targetted entity that will be used to the join. The current implementation does not
    support the referencedColumnName attribute, so Hibernate will join to the primary
    key (default value in the specification).


    The association may be bidirectional. In a bidirectional relationship, one of the side
    has to be the owner : the owner is responsible for the association column(s) update. To
    declare a side as non responsible for the relationship, the attribute mappedBy
    is used. mappedBy refers the property name of the association on the owner side.
    In our case, this is 'passport'. As you can see, you don't have to (must not) declare the
    joining column since it is already on the other side.


    If no @JoinColumn is declared on the owner side, the defaults apply. A join
    column(s) will be created in the owner table and its name will be the concatenation of the
    name of the relationship in the owner side, '_' (underscore), and the name of the primary
    key column(s) in the owned side. In this example 'passport_id' because the property name is
    passport and the column id of Passport is 'id'.



    4.4.2 Many-to-one



    Many-to-one associations are declared at the property level with the annotation
    @ManyToOne; it has a parameter named targetEntity which describes the
    target entity name. You usually don't need this parameter since the default value (the
    type of the property that stores the association) is good in almost all cases:





    @Entity()
    public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.CREATE, CascadeType.MERGE} )
    @JoinColumn(name="COMP_ID")
    public Company getCompany() {
    return company;
    }
    ...
    }



    The @JoinColumn attribute is optional, the default value(s) is like in one to one,
    the concatenation of the
    name of the relationship in the owner side, '_' (underscore), and the name of the primary
    key column in the owned side. In this example 'company_id' because the property name is
    company and the column id of Company is 'id'.


    In case of a bidirectional, a many to one is always the owner side.



    4.4.3 One-to-many



    One-to-many associations are declared at the property level with the annotation
    @OneToMany. In the current EJB3 specification, you can map Collection
    and Set (Map and List are not defined in the spec so far).
    Unless the collection is a generic, you'll have to defined targetEntity. One to many
    associations may be bidirectional.



    4.4.3.1 Bidirectional

    Since many to one are always the owner side of a bidirectional relationship in the EJB3 spec,
    the one to many association is annotated by @OneToMany( mappedBy=... )


    @Entity
    public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
    }

    @Entity
    public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...


    Troop has a bidirectional one to many relationship with Soldier
    through the 'troop' property. You don't have to (must not) define any physical
    mapping in the mappedBy side.



    4.4.3.2 Unidirectional

    A unidirectional one to many using a foreign key column in the owned entity is not
    so common and not really recommanded. We strongly advise you to use a join table
    for this kind of association (as explained in the next section). This kind of
    association is described through a @JoinColumn



    @Entity
    public class Customer implements Serializable {
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinColumn(name="CUST_ID")
    public Set<Ticket> getTickets() {
    ...
    }

    @Entity
    public class Ticket implements Serializable {
    ... //no bidir
    }


    Customer describes a unidirectional relationship with Ticket using the
    join column 'CUST_ID'.



    4.4.3.3 Unidirectional with join table

    A unidirectional one to many with join table is much preferred. This association
    is described through a @AssociationTable.


    @Entity()
    public class Trainer {
    @OneToMany
    @AssociationTable(
    table=@Table(name="TrainedMonkeys"),
    joinColumns = { @JoinColumn( name="trainer_id") },
    inverseJoinColumns = @JoinColumn( name="monkey_id")
    )
    public Set<Monkey> getTrainedMonkeys() {
    ...
    }

    @Entity
    public class Monkey {
    ... //no bidir
    }


    Trainer describes a unidirectional relationship with Monkey using the
    join table 'TrainedMonkeys', with a foreign key 'trainer_id' to Trainer (joinColumns)
    and a foreign key 'monkey_id' to Monkey (inversejoinColumns).



    4.4.3.4 Default values


    Without describing any physical mapping, a unidirectional one to many
    with join table is used. The table name is the concatenation of the owner
    table name, '_', and the other side table name. The foreign key name(s) referencing
    the owner table is the concatenation of the owner table, '_', and the owner
    primary key column(s) name. The foreign key name(s) referencing the other side is the
    concatenation of the owner property name, '_', and the other side primary key
    column(s) name. A unique constraint is added to the foreign key referencing the
    other side table to reflect the one to many.




    @Entity()
    public class Trainer {
    @OneToMany
    public Set<Tiger> getTrainedTigers() {
    ...
    }

    @Entity
    public class Tiger {
    ... //no bidir
    }


    Trainer describes a unidirectional relationship with Tiger using the
    join table 'Trainer_Tiger', with a foreign key 'trainer_id' to Trainer
    (table name, '_', trainer id) and a foreign key 'trainedTigers_id' to Monkey
    (property name, '_', Tiger primary column).



    4.4.4 Many-to-many



    4.4.4.1 description


    A many-to-many association is defined logically using the @ManyToMany annotation.
    You also have to describe the association table and the join conditions using the
    @AssociationTable annotation. If the association is bidirectional, one side has to be
    the owner and one side has to be the inverse end (ie. it will be ignored when updating the
    relationship values in the association table):





    @Entity()
    public class Employer implements Serializable {
    @ManyToMany(
    targetEntity="org.hibernate.test.metadata.manytomany.Employee",
    cascade={CascadeType.CREATE, CascadeType.MERGE}
    )
    @AssociationTable(
    table=@Table(name="EMPLOYER_EMPLOYEE"),
    joinColumns={@JoinColumn(name="EMPER_ID")},
    inverseJoinColumns={@JoinColumn(name="EMPEE_ID")}
    )

    public Collection getEmployees() {
    return employees;
    }
    ...
    }







    @Entity()
    public class Employee implements Serializable {
    @ManyToMany(
    cascade={CascadeType.CREATE, CascadeType.MERGE},
    mappedBy="employees"
    )
    public Collection getEmployers() {
    return employers;
    }
    }



    We've already shown the many declarations and the detailed attributes for associations.
    We'll go deeeper in the @AssociationTable description, it defines a @Table,
    an array of join columns (an array in annotation is defined using { A, B, C }),
    and an array of inverse join columns. The latter ones are the columns of the association
    table which refer to the Employee primary key (the "other side"). Note that the current implementation
    mandates that you join the entity primary keys, referencedColumnName for
    other non pk columns is not supported.


    As seen previously, the other side don't have to (must not) describe the physical
    mapping: a simple mappedBy argument containing the owner side property name bind
    the two.



    4.4.4.1 Default values


    As any other annotations, most values are guessed in a many to many relationship.
    Without describing any physical mapping in a unidirectional many to many
    the following rules applied. The table name is the concatenation of the owner
    table name, '_' and the other side table name. The foreign key name(s) referencing the owner
    table is the concatenation of the owner table name, '_' and the owner primary key
    column(s). The foreign key name(s) referencing the other side is the concatenation of
    the owner property name, '_', and the other side primary key column(s). These are
    the same rules used for a unidirectional one to many relationship.





    @Entity()
    public class Store {
    @ManyToMany(cascade = CascadeType.PERSIST)
    public Set<City> getImplantedIn() {
    ...
    }

    @Entity()
    public class City {
    ... //no bidirectional relationship
    }


    A 'Store_Table' is used as the join table. The 'Store_id' column is a foreign key
    to the 'Store' table. The 'implantedIn_id' column is a foreign key to the 'City' table.


    Without describing any physical mapping in a bidirectional many to many
    the following rules applied. The table name is the concatenation of the owner
    table name, '_' and the other side table name. The foreign key name(s) referencing the owner
    table is the concatenation of the other side property name, '_', and the owner primary
    key column(s). The foreign key name(s) referencing the other side is the concatenation of
    the owner property name, '_', and the other side primary key column(s). These are
    the same rules used for a unidirectional one to many relationship





    @Entity()
    public class Store {
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    public Set<Customer> getCustomers() {
    ...
    }

    @Entity()
    public class Customer {
    @ManyToMany(mappedBy="customers")
    public Set<Store> getStores() {
    ...
    }


    A 'Store_Customer' is used as the join table. The 'stores_id' column is a foreign key
    to the 'Store' table. The 'customers_id' column is a foreign key to the 'City' table.


    Check the unit tests contained in the manytomany package,

    4.4.5 Transitive persistence with cascading



    You probably have noticed the cascade taking an array of CascadeType as a value.
    The cascade concept in EJB3 is very is similar to the transitive persistence and cascading
    of operations in Hibernate, but with slighty different semantics and cascading types:




    • CascadeType.PERSIST: cascades the persist (create) operation to associated entities persist() is called or if the entity is managed
    • CascadeType.MERGE: cascades the merge operation to associated entities if merge() is called or if the entity is managed
    • CascadeType.REMOVE: cascades the remove operation to associated entities if delete() is called
    • CascadeType.REFRESH: cascades the refresh operation to associated entities if refresh() is called
    • CascadeType.ALL: all of the above


    Please refer to the chapter 6.3 of the EJB3 specification for more information on
    cascading and and create/merge semantics.



    4.4.6 Association fetching



    You have the ability to either eagerly or lazily fetch associated entities. The fetch
    parameter can be set to FetchType.LAZY or FetchType.EAGER. EAGER will
    try to use an outer join select to retrieve the associated object, while LAZY is the
    default and will only trigger a select when the associated object is accessed for the first time.



    4.4.7 Collection specific extensions


    It is possible to set

    • the batch size for collections using @BatchSize

    • the where clause, using @Where

    • the check clause, using @Check

    • the caching strategy through the @Cache annotation.


    Please refer to the previous descriptions of these annotations
    for more informations



    4.5 Mapping composite primary and foreign keys



    Composite primary keys use a component class as the primary key representation, so you'd use
    the @Id and @Embeddable annotations. Alternatively, you can use the
    @EmbeddedId annotation. Note that the dependent class has to be
    serializable. This implementation only support composite identifiers with component classes.





    @Entity
    public class RegionalArticle implements Serializable {

    @Id(generate = GeneratorType.NONE)
    public RegionalArticlePk getPk() { ... }
    }

    @Embeddable(access = AccessType.FIELD)
    public class RegionalArticlePk implements Serializable { ... }


    or alternatively




    @Entity
    public class RegionalArticle implements Serializable {

    @EmbeddedId()
    public RegionalArticlePk getPk() { ... }
    }

    public class RegionalArticlePk implements Serializable { ... }



    @Embeddable can define either a field or a property access strategy for the
    component. Composite foreign keys (if not using the default sensitive values) are defined
    on associations using the @JoinColumns element, which is basically an array
    of @JoinColumns. It is considered a good practice
    to express referencedColumnNames explicitely. Otherwise, Hibernate will suppose
    that you use the same order of columns as in the primary key declaration.





    @Entity(access = AccessType.FIELD)
    public class Parent implements Serializable {
    @Id
    public ParentPk id;
    public int age;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumns ({
    @JoinColumn(name="parentCivility", referencedColumnName = "isMale"),
    @JoinColumn(name="parentLastName", referencedColumnName = "lastName"),
    @JoinColumn(name="parentFirstName", referencedColumnName = "firstName")
    })

    public Set<Child> children; //unidirectional
    ...
    }







    @Entity(access = AccessType.FIELD)
    public class Child implements Serializable {
    @Id(generate = GeneratorType.AUTO)
    public Integer id;

    @ManyToOne()
    @JoinColumns ({
    @JoinColumn(name="parentCivility", referencedColumnName = "isMale"),
    @JoinColumn(name="parentLastName", referencedColumnName = "lastName"),
    @JoinColumn(name="parentFirstName", referencedColumnName = "firstName")
    })

    public Parent parent; //unidirectional
    }







    @Embeddable(access = AccessType.FIELD)
    public class ParentPk implements Serializable {
    String firstName;
    String lastName;
    ...
    }





    Note the explicit usage of the referencedColumnName.



    A many-to-many association works the same way, you can find an example in
    Man.java,
    ManPk.java,
    Woman.java,
    WomanPk.java

    4.6 Mapping secondary tables



    You can map a single entity bean to several tables using the @SecondaryTable
    or @SecondaryTables class level annotations. To express that a column is in
    a particular table, use the secondaryTable parameter of @Column or
    @JoinColumn. You can find some examples in
    Cat.java,
    Life.java,
    Death.java

    5. Mapping Queries


    You can map EJBQL/HQL queries using annotations. @NamedQuery and
    @NamedQueries can be defined at the class or at the package level.
    However their definition is global to the session factory scope.
    A named query is defined by its name and the actual query string.



    @javax.persistence.NamedQueries(
    @javax.persistence.NamedQuery(name="plane.getAll", queryString="select p from Plane p")
    )
    package org.hibernate.test.annotations.query;

    ...

    @Entity
    @NamedQuery(name="night.moreRecentThan", queryString="select n from Night n where n.date >= :date")
    public class Night {
    ...
    }

    public class MyDao {
    doStuff() {
    Query q = s.getNamedQuery("night.moreRecentThan");
    q.setDate( "date", aMonthAgo );
    List results = q.list();
    ...
    }
    ...
    }



    5. Compliance and limitations




    3.0 Preview beta1 (07-04-2005) based on the EJB3 Early Draft 2
    --------------------------------------------------------------
    * support parameters in @Type (HBX-197)
    * support @TypeDef at package and class level
    * HBX-166 support @Lob for Character[],char[], String, byte[] and Byte[] (experimental)
    * HBX-159/HBX-140 add @Filter(s) and @FilterDef(s) (Matthew Inger, Magnus Sandberg)
    * HBX-44 @OneToOne support composite PK
    * @OneToOne is supported except for true bidirectional @OneToOne
    * Add @Cache annotation: allow to define caching on root entities and on collections (,eg @Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL, region="specificCacheRegion") )
    * Support @OneToMany default (ie using an association table)
    * HBX-164 insertable/updatable of @JoinColumn now work in @ManyToOne processing (Mario Ivankovits, Emmanuel Bernard)
    * HBX-153 @Id(generate=GeneratorType.AUTO, generator="my_potential_sequence") now work (Pablo Nussembaum)
    * Support @ManyToMany wo @AssociationTable (ie defaults)
    * Support @ManyToMany(mappedBy)
    * Support @OneToMany(mappedBy) (no JoinColumn needed on the @OneToMany side)
    * Appropriate default value when no @JoinColumn is defined in a ManyToOne
    * rename @GeneratorTable to @GeneratedIdTable
    * rename CREATE to PERSIST, add REFRESH cascade style
    * support Mapping Defaults for Non-Relationship Fields or Properties algorithm as defined in the EJB3 spec
    * support @Serialized
    * support @Lob for java.sql.Clob and java.sql.Blob
    * allow embedded object declaration wo @Embeddable (if @Embedded or @EmbeddedId is present in the property)
    * support for @EmbeddedId
    * rename DependentAttribute to AttributeOverride, Dependent to Embedded and DependentObject to Embeddable
    * support @ManyToOne in embedded objects
    * support for @NamedQuery and @NamedQueries (EJBQL)
    * move javax.ejb.* into javax.persistence.* and update copyright header

    3.0alpha3 (28-02-2005)
    ----------------------
    * HBX-116 Support for Where clause in classes and collections @Where(clause="")
    * HBX-115 Support for class proxying configuration: @Proxy(lazy=false, proxyClassName="my.Interface")
    * HBX-88 Support for hibernate type abstraction through @Type (only on basic properties for now)
    * HBX-108 Support @BatchSize(size=n) for entities and collections
    * HBX-107 implements @org.hibernate.annotations.Entity
    * HBX-103 handle abstract classes
    * HBX-83 precision & scale support for column (Bogdan Ghidireac)

    3.0alpha2 (25-01-2005)
    ----------------------
    * HBX-61 Support for @UniqueConstraint (except primaryKey=true)
    * HBX-60 Support for a proper @TableGenerator (using MultipleHiLoPerTableGenerator)
    * HBX-63 Support @GeneratorTable
    * HBX-68 Add declarative configuration of annotated classes
    * HBX-74 Rollback the HB-1315 fix: dialect no longer have to be set in hibernate.properties


    Hibernate-annotations-3.0alpha1 based on the EJB3 Early Draft 1 (6.01.2005)
    ---------------------------------------------------------------------------
    * Support for EJB3 annotations:
    - @Transient
    - @Column (not primaryKey)
    - @JoinColumn (referencedColumnName - only for a reference to a PK, not primaryKey)
    - @Version
    - @Basic
    - @Entity
    - @Table (not uniqueConstraints)
    - @AccessType
    - @Id
    - @CascadeType
    - @FetchType
    - @GeneratorType (NONE, IDENTITY, TABLE, SEQUENCE)
    - @TableGenerator (with scope visibility)
    - @SequenceGenerator (with scope visibility, does not support initialValue() and allocationSize())
    - *not* @GeneratorTable (will have to write a new TableHiloGenerator, but it can wait)
    - @ManyToOne (not optional)
    - @OneToMany (Set and Collection, generics version or not, JoinColumn not guessed)
    - @OneToOne
    but not optional
    no composite PK/FK
    - @ManyToMany
    - @AssociationTable (Has to be on both sides)
    - @Inheritance
    - @InheritanceType (has to be defined on every classes of the hierarchy for JOINED strategy,
    not very clear about the TABLE_PER_CLASS strategy)
    - @DiscriminatorColumn
    - @DiscriminatorType
    - @InheritanceJoinColumn
    - @InheritanceJoinColumns
    this annotation for Composite PK Entities has to be explicit, I do not respect the implicit semantic of the EJB3 spec
    - @SecondaryTable (@OneToMany @JoinColumn(secondaryTable="..." does not work yet due to H3 core issue HHH-36
    - @SecondaryTables
    this annotation for Composite PK Entities has to be explicit, I do not respect the implicit semantic of the EJB3 spec
    - @DependentObject
    - @Dependent
    - @DependentAttribute (only for basic properties as per the spec)
    - @Id in conjunction with @DependentObject (composite primary keys)
    - @JoinColumns in conjunction with @ManytoOne, @OneToMany, @ManytoMany
    - note that the composite FK columns have to be in the same table (no != secondary tables). This is probably a weird case and certainly a not recommanded one.


    Still missing or incomplete features compared to the EJB3 spec
    --------------------------------------------------------------
    - use of referencedColumnName for column other than the PK ones (HX-62)
    - better semantic of cascade (HBX-47)
    - Support for a true bidirectional one to one relationship (HBX-177)
    - inheritance declaration parsing should be more robust (HBX-186)
    - annotations inheritance and superclasses (HBX-157) (to be addressed by the next draft)
    - appropriate fetching mode on queries (HBX-87)
    - implicit join column declaration in inheritance with composite PK (HBX-75)
    - @UniqueConstraint(primaryKey=true) (HBX-82)
    - support for initialValue and allocationSize in @SequenceGenerator (HBX-59)
    - finish support of optional=false (HBX-190)
    - @IdClass (HBX-213)


    6. Useful links



    Hibernate Annotations API

    The EJB3 Specification

    The EJB3 JBoss tutorial

    The Hibernate reference guide

    Hibernate In Action



  • Posted by 아름프로
    Posted by 아름프로


    Working with Hibernate in Eclipse
    Working with Hibernate in Eclipse


    by James Elliott, author of Hibernate: A Developer's Notebook

    01/05/2005





    Editor's Note: With our survey results showing a huge interest in Hibernate, we thought this would be a good week to bring back this piece, by the author of O'Reilly's Hibernate book, on how to use Hibernate with Eclipse, which was also a top vote-getter in the poll.



    Introduction





    I recently started using Eclipse as my
    development environment, in part because of its support for the many platforms
    on which I develop, and in part because Eclipse is a great example of the power of
    an open, extensible environment in which people all around the world can
    contribute. I'm beginning to investigate the extensions people have come up
    with. For example, I use a little plugin called XMLBuddy to work with XML files, and it's very helpful. So I became curious about whether anyone had written plugins to work
    with Hibernate, since I've done so much of
    that recently in putting together the Developer's Notebook. It turns out there are several such efforts underway; in this article we will
    explore one of them--the Hibernate Synchronizer.



    Hibernate Synchronizer



    Of the plugins I've found so far, the Hibernate Synchronizer
    interested me most because it seems to best support the kind of mapping-centric
    workflow I adopted throughout my Developer's Notebook. (Hibernate can be used
    in many different ways, so check out the
    other plugins available; these may be more helpful if your environment calls for another approach.) In fact, the Hibernate Synchronizer plugin removes the need for you to think about updating your Java code when you change your mapping document. In a very Eclipse-like way, it
    automatically updates the Java code as you edit the mapping. But it goes even
    farther than Hibernate's built-in code generation tools by creating a
    pair of classes for each mapped object. It "owns" a base class, which
    it rewrites at will as you change the mapping, and gives you a subclass that
    extends this base class, where you can put business logic and other code,
    without fear that it will ever get changed out from under you.


























    Contents
    Introduction
    Hibernate Synchronizer
       Installation
       Configuration
       Generating Code
       Editing Mappings
       Generating the Database Schema
       Trade-Offs
    Other Plugins
       HiberClipse
       Hibernator
    Learning More




    As befits an approach centered around the Hibernate mapping document,
    Hibernate Synchronizer includes a new editor component for Eclipse that provides
    intelligent assistance and code completion for such documents. A nice DTD-driven
    XML editor, such as the aforementioned XMLBuddy, can do some of this for you, but
    Hibernate Synchronizer uses its semantic understanding of the documents to go
    much further. It also offers a graphical view of the properties and relations in
    the mapping, "wizard" interfaces for creating new elements, and other such
    niceties. And, as mentioned, in its default configuration the editor
    automatically regenerates the data-access classes as you edit their mapping
    documents.



    There are other pieces to Hibernate Synchronizer, too. It adds a section to
    Eclipse's New menu that provides wizards for creating Hibernate
    configuration and mapping files, and adds contextual menu entries in the package
    explorer and in other appropriate places, providing easy access to relevant
    Hibernate operations.



    OK, enough abstract description, time to get down to the practical stuff!
    After all, you were already probably interested in this, or you wouldn't have
    started to read the article. So how do you get and play with Hibernate Synchronizer?



    Installation



    Hibernate Synchronizer is installed using Eclipse's built-in Update Manager.
    The plugin offers separate update sites for users of Eclipse 2.1 and the
    forthcoming Eclipse 3. Because I'm using Eclipse for mission-critical work, I'm
    still using the production release, 2.1. As I write this, Eclipse 3 has entered
    its "release candidate" phase, and I am very much looking forward to being able
    to upgrade to a production release of version 3 when I return from JavaOne
    later this summer. (The main reason I mention this is to emphasize that the
    following instructions are written from an Eclipse 2 perspective; some commands
    and screens are undoubtedly different in Eclipse 3, so if you're using it, be
    sure to apply your own judgment in following these steps! If it helps, my
    impression is that Hibernate Synchronizer's own
    install instructions are written for Eclipse 3.)



    Fire up Eclipse and open the Update Manager by choosing Help
    -> Software Updates -> Update Manager. Once the
    Install/Update perspective opens up, right-click (or control-click, if you're
    using a one-button mouse) in the Feature Updates view and choose
    New -> Site Bookmark, as shown in Figure 1.



    Figure 1

    Figure 1. Adding the Hibernate Synchronizer plugin site to the Update Manager



    In the resulting dialog, enter the URL for the version of the plugin that you need. The URL to be entered depends on your Eclipse version:




    • Eclipse 2.1: http://www.binamics.com/hibernatesync/eclipse2.1

    • Eclipse 3: http://www.binamics.com/hibernatesync



    You also need to assign a name for the new bookmark. "Hibernate Synchronizer"
    makes a lot of sense. Figure 2 shows the dialog with all required information in
    my Eclipse 2.1.2 environment. Once you've got it filled in, click
    Finish to add the bookmark.



    Figure 2

    Figure 2. Bookmark for the Hibernate Synchronizer plugin update site



    Once you click Finish, the new bookmark will appear in the Feature Updates
    view, as shown in Figure 3.



    Figure 3

    Figure 3. The Hibernate Synchronizer site is now available for use



    To actually install the plugin, click on the disclosure triangle to the left
    of the bookmark, and again on the next one that appears inside of it, until you can
    see the icon for the plugin itself. When you click on that, the Preview view
    will update to show you an interface that allows you to install the plugin, as
    shown in Figure 4.



    Figure 4

    Figure 4. Ready to install the plugin



    Click Install Now to actually install it, and let Eclipse walk
    you through the process (Figures 5-10).



    Figure 5

    Figure 5. Installing Hibernate Synchronizer



    Figure 6

    Figure 6. The plugin license agreement



    See Trade-Offs, below, for some discussion about this license agreement. You may wish to read it carefully before deciding to use Hibernate Synchronizer in a project of your
    own. I think it's probably fine, but it is confusingly based on the GPL without
    actually being open source.



    Figure 7

    Figure 7. Choosing where to install the plugin; the default is fine



    Figure 8

    Figure 8. The standard warning for unsigned plugins



    Figure 9

    Figure 9. The install is underway



    Figure 10

    Figure 10. The install has completed



    Now that the plugin is installed, you need to quit and relaunch Eclipse for
    it to take effect. The dialog seems to imply that Eclipse will restart itself,
    but in my experience, clicking Yes merely causes the environment to quit, and you
    have to relaunch it manually. This may be a limitation of Eclipse 2.1's Mac OS
    X implementation; Eclipse 3 is going to be the first release that promises
    "first-class" support for OS X. In any case, this is a very minor issue. If you
    need to restart Eclipse, do so now, because it's time to start configuring the
    plugin to put it through its paces!





















    Configuration



    Once Eclipse comes back up, you can close the Install/Update perspective. Open
    a Java project that uses Hibernate. If you've been going through the examples in
    the Developer's Notebook, you'll have several directories from which to choose. I'll
    be looking at the examples as they exist in Chapter 3, which is the sample
    chapter available
    online.
    You can also download the source for all of the examples from the book's site.



    If you're creating a new Eclipse project to work with one of the example
    source directories, just choose File -> New ->
    Project, specify that you want to create a Java project and click
    Next, give it a name ("Hibernate Ch3" in my case, as shown in
    Figure 11), uncheck the Use default checkbox so that you can tell
    Eclipse where to find the existing project directory, and hit the
    Browse button to locate where it exists on your own drive. At this
    point, you can click Finish to create the project, but I generally
    like to click Next and double-check the decisions Eclipse is
    making. (Of course, if it gets anything wrong, you can always go back and fix
    the project properties, but I tend to find it disconcerting to be greeted by a
    ton of errors and warnings immediately if there is a library missing or
    something.)



    Figure 11

    Figure 11. Creating a new project to work with Hibernate



    In this case, my caution was unnecessary. Eclipse figured out exactly how the
    directory was structured and intended to be used, and found all of the third-party
    libraries I had downloaded and installed in order to enable Hibernate and the
    HSQLDB database engine to run. (A detailed walkthrough of this process is the
    bulk of Chapter 1 of my Developer's Notebook.) This kind of smart adaptability
    is one of the great features of Eclipse. Figure 12 shows our new project open
    and ready for experimentation. It also shows that Eclipse doesn't like to fit
    into a window small enough for a reasonable screen shot; I'm going to have to
    work with partial window captures from this point on.



    Figure 12

    Figure 12. The Chapter 3 example project



    The next thing we need to do is create a Hibernate configuration file that
    Hibernate Synchronizer can use. There is already a hibernate.properties
    file in the src directory, which is how the examples in the book work,
    but Hibernate Synchronizer only works with Hibernate's XML-based configuration
    approach. So we'll need to replicate the contents of hibernate.properties into a new hibernate.cfg.xml file. On the bright side, this gives us our first opportunity to play with a feature of
    Hibernate Synchronizer, the configuration file wizard. Choose File
    -> New -> Other, click the newly available
    Hibernate category, pick Hibernate Configuration File,
    and click Next.



    Figure 13

    Figure 13. Starting the Hibernate Configuration File wizard



    When the wizard starts up, the directory it offers to put the file into
    depends on the file you've currently got selected in Eclipse. Let's be sure to
    put it at the top-level src directory alongside the properties version, for
    consistency. Fill in the rest of the information requested by the wizard to
    match the properties version of the configuration, as shown in Figure 14. Notice
    that, unlike when using Ant to control the execution of Hibernate (which was the
    approach used in the Developer's Notebook), we have no way to control the
    current working directory when Hibernate is invoked, so we need to use a
    fully qualified path to the database file in the URL. In my case, this takes the
    (somewhat ungainly) value jdbc:hsqldb:/Users/jim/Documents/Work/OReilly/Hibernate/Examples/ch03/data/music.
    (If anyone can tell me how to get Eclipse or Hibernate Synchronizer to use a
    particular working directory for a project, I'd certainly be interested. I'm
    still a beginner when it comes to Eclipse, so it would not surprise me at all to
    learn that this is possible and that I simply don't know how to do it.)



    Figure 14

    Figure 14. Filling in the configuration file details



    Filling in the Driver Class is a little strange: You need to click the
    Browse button, and start typing the driver name. If you type
    "jdbcD", the window will present only two choices, and you can easily click the
    right one. This is illustrated in Figure 15.



    Figure 15

    Figure 15. Specifying the HSQLDB driver class



    Once the wizard is set up to the extent of Figure 14, with values appropriate
    for your own installation, you can click Finish to create the
    configuration file. Hibernate Synchronizer is now ready to use. It opens the
    file it created so you can see the structure and details of an XML configuration
    file for Hibernate.




    Figure 16

    Figure 16. The generated configuration file



    A quick way to test that the configuration is working is to play with the
    other wizard interface. Choose File -> New ->
    Other, click the newly available Hibernate category,
    pick Hibernate Mapping File, and click Next. When the
    wizard comes up, it should be populated with the settings information we just
    entered, and you can click the Refresh button to make sure it can
    communicate with the database and show you that it found a TRACK
    table. The first time you do this, you might have to confirm the location of the
    .jar file containing the HSQLDB driver, for some reason, but that seems to happen
    only once. In any case, once you confirm that everything seems to be working,
    click Cancel rather than actually creating the mapping, because we
    want to work with our hand-created mapping file that already exists.





















    Generating Code



    This is probably the part you've been waiting for. What cool stuff can we do?
    Well, right away there is a new contextual menu entry available for Hibernate
    mapping documents.



    If you right-click (or control-click) on one, you get a
    number of Hibernate-related choices (Figure 17), including one to synchronize.
    This is a manual way to ask Hibernate Synchronizer to generate the data access
    objects associated with the mapping document.



    Figure 17

    Figure 17. Synchronizer choices for mapping documents



    The Add Mapping Reference choice is also useful: it adds an
    entry to the main Hibernate configuration file telling it about this mapping
    document, so you don't need to put anything in your source code to request that
    the corresponding mapping gets set up. For now, let's look at the result of
    choosing Synchronize Files.



    This is where things start to get interesting. We end up with two new
    sub-packages, one for the "base" data access objects that Hibernate Synchronizer
    "owns" and can rewrite at any time, and one for our business objects that
    subclass these DAOs, which will not get overwritten, and give us an opportunity
    to add business logic to the data class (shown in Figure 18).



    Figure 18

    Figure 18. The synchronized data access objects, showing our editable subclass



    There are many more classes generated this way than by using the normal
    Hibernate code generation facilities, which has advantages, as well as some
    potential disadvantages, which I discuss later in the
    Trade-Offs
    section. Note also that in the properties configuration for your project, you can
    choose which of these classes get generated for you, as well as the package
    structure into which they are generated. I'd demonstrate this, but the current
    release of the plugin has a bug
    which blocks access to this configuration interface on Mac OS X. A fix has been
    made, but not yet released.



    Based on the examples on the Hibernate Synchronizer page, I put together the
    following class to try inserting some data into the music database using these
    new data access objects. It's quite similar to the version using the standard
    Hibernate code generator (on pages 39-40 of Hibernate: A Developer's Notebook) and even simpler because the classes generated by Hibernate Synchronizer create and commit a new transaction for each of your database
    operations, so you don't need any code to set one up in simple situations like
    this. (There are ways of doing so if you need to have a group of operations
    operate as a single transaction, of course.) Here's the code for the new
    version:



    package com.oreilly.hh;

    import java.sql.Time;
    import java.util.Date;
    import net.sf.hibernate.HibernateException;
    import com.oreilly.hh.dao.TrackDAO;
    import com.oreilly.hh.dao._RootDAO;

    /**
    * Try creating some data using the Hibernate Synchronizer approach.
    */
    public class CreateTest2 {

    public static void main(String[] args) throws HibernateException {
    // Load the configuration file
    _RootDAO.initialize();

    // Create some sample data
    TrackDAO dao = new TrackDAO();
    Track track = new Track("Russian Trance", "vol2/album610/track02.mp3",
    Time.valueOf("00:03:30"), new Date(), (short)0);
    dao.save(track);

    track = new Track("Video Killed the Radio Star",
    "vol2/album611/track12.mp3", Time.valueOf("00:03:49"), new Date(),
    (short)0);
    dao.save(track);

    // We don't even need a track variable, of course:
    dao.save(new Track("Gravity's Angel", "/vol2/album175/track03.mp3",
    Time.valueOf("00:06:06"), new Date(), (short)0));
    }
    }


    Having Eclipse around while I was writing this was very nice. I'd forgotten
    how much I missed intelligent code completion while I was writing the examples
    for the book, and there are several other things the JDT helps with too.



    To run this simple program within Eclipse, we need to set up a new Run
    configuration. Choose Run -> Run... with
    CreateTest2.java as the currently active editor file. Click on
    New and Eclipse figures out that we want to run this class in our
    current project, because we created it with a main() method. The
    default name it assigns, CreateTest2, is fine. The screen will look
    something like Figure 19. Click Run to try creating some data.



    Figure 19

    Figure 19. Ready to run our creation test in Eclipse



    If you've been exactly following along on your own, you'll find that this
    first attempt at execution fails: Hibernate complains that the configuration
    file contains no mapping references, and at least one is required. Ah ha! So
    that's what XMLBuddy was warning about with the yellow underline near
    the bottom of Figure 16. We can easily fix this by right-clicking on the Track.hbm.xml
    mapping document in the Package Explorer view and choosing Add Mapping
    Reference in the new Hibernate Synchronizer submenu. That makes XMLBuddy
    happy, and allows the run to get further. Unfortunately, not as far as we might
    like, though. The next error was a complaint about not being able to find the
    JTA UserTransaction initial context in JNDI. It turned out I wasn't
    the only person having this problem; it was discussed in a forum thread, but no one had yet found a solution.



    Since I knew I didn't need to use JTA, I wondered why Hibernate was even
    trying. I opened up the Hibernate configuration file (Figure 16) and looked for anything suspicious that Hibernate Synchronizer had put there. Sure enough, there were some lines that looked like prime suspects:



     <property name="hibernate.transaction.factory_class"> 
    net.sf.hibernate.transaction.JTATransactionFactory
    </property>
    <property name="jta.UserTransaction">
    java:comp/UserTransaction
    </property>


    Once I tried commenting these out and running again, the third time was
    indeed the charm. My run completed with no errors, and my data appeared in the
    database. Hurrah! Running the trusty ant db target
    (explained in Chapter 1 of the Developer's Notebook) reveals the data in all its
    (admittedly simple) glory, as shown in Figure 20. If you're doing this yourself,
    be sure to start with an ant schema to create the
    database schema or empty out any test data that may be there from previous
    experimentation.



    Figure 20

    Figure 20. The data created by our test program



    Note that you can run Ant targets from within Eclipse by right-clicking (or
    control-clicking) on the build.xml file within the Package Explorer,
    choosing Run Ant, and picking the target using an Eclipse dialog.
    Pretty cool.



    Figure 21

    Figure 21. Running Ant from within Eclipse



    Getting data back out using queries is pretty straightforward, although this
    time it's a lot closer to the same code you'd use with the ordinary
    Hibernate-generated data access classes. Even though Hibernate Synchronizer
    generates a number of helper methods for working with named queries, I don't
    think any of them is particularly useful, because they all insist on running the
    query and returning the list of results, rather than giving you the
    Query object to work with yourself. That prevents you from using
    any of Query's convenient type-safe parameter setting methods.
    Because of that, I decided to stick to having the _RootDAO object
    give me a Hibernate Session to work with the "old fashioned" way.
    In fairness, I think I could edit the templates used by Hibernate Synchronizer
    to generate any methods I'd like, and would almost certainly look into doing
    that if I was going to undertake a project with it.





















    Actually, on further reflection, because you can only work with a
    Query while you've got an active Session, the methods
    offered by the DAOs already work the best way they possibly can. You're always
    going to have to do your own session management if you want to work with the
    query the way I do in this example. You could embed the session management into
    the business logic provided in "your" half of the DAO, though, which would give
    you the best of both worlds. That's another reason the split-class model offered
    by Hibernate Synchronizer is so useful. I explore this insight a bit href="#betterQuery">below.



    Anyway, here's the code I first came up with, morally quite equivalent to
    that on pages 48-49 of the book:



    package com.oreilly.hh;

    import java.sql.Time;
    import java.util.ListIterator;

    import net.sf.hibernate.HibernateException;
    import net.sf.hibernate.Query;
    import net.sf.hibernate.Session;

    import com.oreilly.hh.dao.TrackDAO;
    import com.oreilly.hh.dao._RootDAO;

    /**
    * Use Hibernate Synchronizer's DAOs to run a query
    */
    public class QueryTest3 {

    public static void main(String[] args) throws HibernateException {
    // Load the configuration file and get a session
    _RootDAO.initialize();
    Session session = _RootDAO.createSession();

    try {
    // Print the tracks that will fit in five minutes
    Query query = session.getNamedQuery(
    TrackDAO.QUERY_COM_OREILLY_HH_TRACKS_NO_LONGER_THAN);
    query.setTime("length", Time.valueOf("00:05:00"));
    for (ListIterator iter = query.list().listIterator() ;
    iter.hasNext() ; ) {
    Track aTrack = (Track)iter.next();
    System.out.println("Track: \"" + aTrack.getTitle() +
    "\", " + aTrack.getPlayTime());
    }
    } finally {
    // No matter what, close the session
    session.close();
    }
    }
    }


    One nice feature that TrackDAO does give us is a static
    constant by which we can request the named query, eliminating any chances of
    run-time errors due to typos in string literals. I appreciate that! Setting up
    and executing a Run configuration for this test class produces the output I'd
    expect, as shown in Figure 22.



    Figure 22

    Figure 22. The query results in Eclipse's console view



    As I noted above, after getting this class working, I
    realized there was a better way to approach it, given the model offered by
    Hibernate Synchronizer. Here's what our TrackDAO object would look
    like if we moved the query inside of it, which is where it really belongs, given
    that the named query is a feature of the mapping file associated with that data
    access object:



    package com.oreilly.hh.dao;

    import java.sql.Time;
    import java.util.List;

    import net.sf.hibernate.HibernateException;
    import net.sf.hibernate.Query;
    import net.sf.hibernate.Session;

    import com.oreilly.hh.base.BaseTrackDAO;

    /**
    * This class has been automatically generated by Hibernate Synchronizer.
    * For more information or documentation, visit The Hibernate Synchronizer page
    * at http://www.binamics.com/hibernatesync or contact Joe Hudson at joe@binamics.com.
    *
    * This is the object class that relates to the TRACK table.
    * Any customizations belong here.
    */
    public class TrackDAO extends BaseTrackDAO {

    // Return the tracks that fit within a particular length of time
    public static List getTracksNoLongerThan(Time time)
    throws HibernateException
    {
    Session session = _RootDAO.createSession();
    try {
    // Print the tracks that will fit in five minutes
    Query query = session.getNamedQuery(
    QUERY_COM_OREILLY_HH_TRACKS_NO_LONGER_THAN);
    query.setTime("length", time);
    return query.list();
    } finally {
    // No matter what, close the session
    session.close();
    }
    }
    }


    This is nice and clean, and it simplifies the main() method in
    QueryTest3 even more:



        public static void main(String[] args) throws HibernateException {
    // Load the configuration file and get a session
    _RootDAO.initialize();

    // Print the tracks that fit in five minutes
    List tracks = TrackDAO.getTracksNoLongerThan(Time.valueOf("00:05:00"));
    for (ListIterator iter = tracks.listIterator() ;
    iter.hasNext() ; ) {
    Track aTrack = (Track)iter.next();
    System.out.println("Track: \"" + aTrack.getTitle() +
    "\", " + aTrack.getPlayTime());
    }
    }


    Clearly this is the approach to take when working with named queries and
    Hibernate Synchronizer. A quick test confirms that it produces the same output,
    and it's much better code.



    Whether or not you want to use Hibernate Synchronizer to generate its own
    style of data access objects, there is one last major feature to explore.



    Editing Mappings



    One of the main attractions of Hibernate Synchronizer is its specialized
    editor for mapping documents. This editor can be configured to automatically
    regenerate the associated data objects whenever you save files, but that's just
    a final touch; you might want to use the editor even if you're not using the
    plugin's code generator. It gives you smart completion of mapping document
    elements, and a graphical outline view in which you can manipulate them, as
    well.



    There is a trick to getting the editor to work for you, though, at least if
    you're starting from the downloaded source code from my Developer's Notebook. In
    the download, the mapping documents are named with the extension
    ".hbm.xml," and the editor is only invoked for files ending with
    ".hbm". In theory, you can configure the extension mappings within
    Eclipse so that both extensions use the plugin's mapping document editor, but I
    wasn't able to get that to work, and I saw that someone else on the support
    forum had the same problem. So, at least for now, your best bet may be to rename
    the files. (If you're going to stick with Ant-based standard code generation, be
    sure to update the codegen target in build.xml to use the
    new extension, too.)



    As soon as I renamed Track.hbm.xml to Track.hbm, its icon
    in the Package Explorer was updated to look like the Hibernate logo, and the
    default editor became the plugin's, as shown in Figure 23. For whatever reason,
    the other Hibernate Synchronizer options (as shown in Figure 17) are available with either extension, but the editor is available only with the shorter version.



    Figure 23

    Figure 23. The contextual menu for a Hibernate mapping document (with the extension ".hbm")



    The editor has context-sensitive completion support for all of the elements
    you're adding within the mapping document. Figure 24 shows a couple of examples,
    but no screen shots can really capture the depth and usefulness of a feature
    like this; I'd very much encourage you to install the plugin and play with it
    yourself for a while. You will quickly see how helpful it can be in working with
    mapping documents.



    Figure 24



    Figure 25

    Figures 24 and 25. Completion assistance in the mapping document editor



    The outline view, shown in Figure 26, gives you a graphical view of the
    hierarchy of classes, their mapped elements, named queries, and the like that
    are present in your mapping document, as well as giving you a menu offering a
    few wizards to help you create new ones.







    Figure 26 Figure 27


    Figures 26 and 27. The mapping editor's outline view, and the "Add property" wizard



    The contextual menu within the editor itself also offers a Format
    Source Code option you can use to clean up and re-flow the document.
    There are already many neat and useful features in this editor, and it'll be
    interesting to see how it grows in the future. My only complaint (and a minor
    one at that) is that this editor uses a very different approach to helping you
    manage quotation marks when you complete XML attributes than the JDT does in
    Java code. Switching back and forth between them can be somewhat disorienting.
    (The way the JDT works takes a little getting used to itself, but once you start
    trusting it, it's almost magical.)





















    Generating the Database Schema



    Despite my first impression that everything flowed from the mapping document,
    Hibernate Synchronizer doesn't currently offer any support for creating or
    updating a database schema from your mapping documents. There has already been a
    request posted to the support forum about this, and it wouldn't surprise me if
    we saw these features in the future; support shouldn't be too difficult. For
    now, you'll have to stick with an approach like the Ant-driven one in
    Hibernate: A Developer's Notebook if you're developing your
    database from your mappings. Alternately, the Hibernator plugin described
    below
    does support schema updates from within Eclipse. I may have to
    investigate whether it's possible to have both of these plugins installed at the
    same time.



    Well, I certainly hope this whirlwind tour has given you an sense of the
    capabilities offered by the plugin. I haven't covered all of them, by any means,
    so do download it and explore on your own if anything has intrigued you.



    Trade-Offs



    Clearly you can do some neat things with Hibernate Synchronizer. Will I be
    using it for my own Hibernate projects? There are some pluses and minuses to
    that idea, and I probably won't decide until I get to the point of actually
    adopting Hibernate in place of our homebrew (and very simplistic) lightweight
    O/R tool at work. That is going to be a significant enough change that we are
    putting it off until we tackle a major architecture shift that's on the horizon
    for other reasons. Here are some of the factors that will weigh in my
    decision.



    As mentioned in the Installation section, there is a little bit of concern
    about the license out there. The plugin's forum has a
    discussion
    about this. The current license is based on a custom modification of the GNU GPL
    that removes all the source-sharing provisions, but tries to retain the other
    aspects of "copyleft" protection. There is some question about the legitimacy of
    this, and the author is looking for an alternative. It is clear that the
    intention is to protect the plugin, not to encumber any other project that
    happens to use the plugin to generate code, but it may be worth carefully
    reading the current license to see if you believe that intent has been achieved,
    or if there is too much risk for you.



    The same discussion reveals that the author had originally released the
    plugin as open source, but withdrew it temporarily because he felt it wasn't yet
    polished enough to serve as a good example to others. He then had some very
    annoying email interactions with hotheads who, sadly, soured him on the whole
    idea of sharing the source. It is certainly his prerogative to decide what, if
    anything, to share with us. The plugin is a gift to the world, and the author
    doesn't owe us anything. But I hope that enough positive interactions with other
    users might help convince him to go back to his original plan of sharing the
    source. I really value having access to the source code of tools that I use, not
    only because it is a very valuable learning opportunity, but because it
    means I (or others) can fix little problems immediately if we need to. The
    author has been very responsive so far in addressing user concerns, but no one
    person can keep up as well as a community, and we all sometimes get busy, burned
    out, or otherwise distracted.



    The fact that Hibernate Synchronizer uses its own templates and mechanism to
    generate your data access class is both positive and negative. It's positive in
    that it gives you more capabilities than Hibernate's "standard" code generation
    tools. The ability to work with an auto-generated subclass of your data object
    in which you can embed business logic without fear of it getting overwritten
    when you regenerate the access code is a big plus. And there are other niceties
    offered by the plugin's generated classes that make many of the simple cases
    even simpler.



    On the other hand, this also means that Hibernate Synchronizer's generated
    code can lag behind Hibernate when there are new features added or changes made
    to the platform. The plugin's code is also more likely to have bugs in its
    support for Hibernate's less-used modes: it has a much smaller user base, and a
    single person keeping it updated. You can see evidence of this phenomenon on the
    discussion forum.



    As with so many things, it's up to you to decide whether the potential
    benefits outweigh the risks. Even if you don't use the code generator, you might
    find the mapping editor extremely useful. You can turn off automatic
    synchronization if you want to just use the editor's completion and assistance
    features.



    If you do adopt the plugin and find it useful, I would definitely encourage
    you to contact the author and thank him, and consider donating some money to
    help support its further development.



    Other Plugins



    In my hunting so far, I've encountered two more plugins that offer support
    for Hibernate within Eclipse. (If you know of others, or come across them in the
    future, I'd be interested in learning about them.) Perhaps I'll write articles about these in the future.



    HiberClipse



    The HiberClipse plugin
    looks like another very useful tool. It seems geared towards a database-driven
    workflow, where you've already got a database schema and want to build a
    Hibernate mapping file and Java classes to work with it. This is a common
    scenario, and if you find yourself facing such a challenge, I'd definitely
    recommend checking out this plugin. One really cool feature it offers is a
    graphical "relationships view" of the database you're working with, right within
    Eclipse. (I should point out that Hibernate Synchronizer doesn't leave you high
    and dry if you want to start with an existing database schema, either. Its New
    Mapping File Wizard can connect to your database and build the mapping file
    based on what it finds.)



    Figure 28

    Figure 28. Hibernate Synchronizer's Mapping Wizard



    Hibernator



    Finally, Hibernator seems to
    lean in the opposite direction, starting from your Java code to generate a
    simple Hibernate mapping document, and then from there letting you build (or
    update) the database schema. It also offers the ability to run database queries
    within Eclipse. Of the three plugins, it appears to be at the earliest stages of
    development, but already looks worth keeping an eye on, especially since it
    cites members of the Hibernate development team as contributors.



    Learning More



    If I've managed to pique your interest in this article, there are plenty of
    resources to help you dig deeper into these topics. In addition to the sites
    I've linked to throughout the text, there are some books that might interest you.
    Of course, I have to mention my own,
    Hibernate: A Developer's
    Notebook
    . For in-depth reference material about Hibernate, the online documentation is very useful, especially the reference manual, and there is a forthcoming book by the developers of Hibernate itself, Hibernate in Action. I look forward to reading that myself.



    As for Eclipse, I'm currently working through Steve Holzner's Eclipse and looking forward to the Eclipse Cookbook that will be released later this month. My blog discusses my Eclipse "conversion" in more detail in case you're curious about that (or teetering on the edge yourself). If you're just getting started, be sure to explore the
    "Getting Started" sections of Eclipse's built-in Workbench and Java Development
    user guides. These show you how the environment is intended to be used, give you
    some good suggestions, and walk you through processes and features you might not
    otherwise discover quickly on your own. Choose Help -> Help Contents within Eclipse to find them.




    Posted by 아름프로

    BLOG main image

    카테고리

    분류 전체보기 (539)
    이야기방 (19)
    토론/정보/사설 (16)
    IBM Rational (9)
    U-IT (0)
    SOA/WS/ebXML (110)
    개발방법론/모델링 (122)
    J2SE (34)
    J2EE (60)
    DataBase (39)
    Open Projects (30)
    WebWork (0)
    Hibernate (4)
    xdoclet (0)
    jfree (0)
    iBATIS (1)
    Mule (1)
    ServiceMix (0)
    ActiveMQ (1)
    Drools (2)
    JBoss (1)
    XML관련 (8)
    Spring (12)
    BP/표준화 (50)
    Apache Projects (15)
    Web/보안/OS (22)
    Tools (7)
    AJAX/WEB2.0 (1)
    Linux/Unix (1)
    영어 (0)
    비공개방 (0)

    최근에 올라온 글

    최근에 달린 댓글

    최근에 받은 트랙백

    달력

    «   2024/04   »
    1 2 3 4 5 6
    7 8 9 10 11 12 13
    14 15 16 17 18 19 20
    21 22 23 24 25 26 27
    28 29 30

    글 보관함

    Total :
    Today : Yesterday :