This JDK5 annotation library, eliminates hand-written (or XDoclet generated) Hibernate mapping (*.hbm.xml) files. All annotation elements are chosen to be as close as possible to their mapping xml counter part. Please, read the Hibernate docs for explanations on how to use the elements.
The Person class below shows the simplest possible annotated entity class.
@clazz public class Person { public Person() {} public Person(String name) {this.name = name;} @id private Long id; public Long getId() {return id;} public void setId(Long id) {this.id = id;} @property private String name; public String getName() {return name;} public void setName(String name) {this.name = name;} }
The clazz annotation, corresponds to a class XML node in a mapping file. As you can see, there have been some minor deviations from the XML names, simply because they clash with reserved words in Java.
The id and property annotations have their usual meaning. All annotations have many attributes, corresponding to attributes or nested elements of their XML node counter parts. However, the annotation attributes all have sensible default values, either hard coded or extracted from the class/field information.
A Hibernate mapping XML is generated behind the scene, when a class is added to a Hibernate configuration. The snippet below loads the Person class into a Hibernate configuration object. The Configuration object is a subclass to Hibernate's ditto.
Configuration cfg = new Configuration();
cfg.addAnnotatedClass( Person.class );
//...load Hibernate props...
SessionFactory sf = cfg.buildSessionFactory();The mapping XML generated in the example above, is shown below.
<hibernate-mapping>
<class name="com.ribomation.metahibernate.functest.polymorphicAssociation.Person"
table="PERSONS" optimistic-lock="version" mutable="true"
dynamic-update="false" dynamic-insert="false"
select-before-update="false" polymorphism="true"
lazy="false" batch-size="1">
<id name="id" column="ID" access="property" unsaved-value="null">
<generator class="native"/>
</id>
<property name="name" column="NAME" access="property" insert="true" update="true"/>
</class>
</hibernate-mapping>At less simple example, although still non-complex, is the first functional test of this library. You can find the complete source code in Polymorphic Association Functional Test.
This test demonstrates a polymorphic bidirectional 1-M assocation from a Person entity to a Vehicle entity hierarchy.
@clazz
public class Person {
public Person() {}
public Person(String name) {
this.name = name;
}
@id
private Long id;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
@property
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@set(oneToMany = Vehicle.class, cascade = CascadeType.saveUpdate, lazy = false)
private Set vehicles = new HashSet();
public Set getVehicles() {return vehicles;}
public void setVehicles(Set vehicles) {this.vehicles = vehicles;}
public void addVehicle(Vehicle v) {
getVehicles().add(v);
v.setOwner(this);
}
}
@clazz
@discriminator(column = "type")
public abstract class Vehicle {
protected Vehicle() {}
protected Vehicle(int speed) {
this.speed = speed;
}
@id
private Long id;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
@property
private int speed;
public int getSpeed() {return speed;}
public void setSpeed(int speed) {this.speed = speed;}
@manyToOne(column = "person_fk")
private Person owner;
public Person getOwner() {return owner;}
public void setOwner(Person owner) {this.owner = owner;}
}
@subclass
public class Car extends Vehicle {
public Car() {}
public Car(String regNr) {
super(110);
this.regNr = regNr;
}
@property
private String regNr;
public String getRegNr() {return regNr;}
public void setRegNr(String regNr) {this.regNr = regNr;}
}
@subclass
public class Boat extends Vehicle {
public Boat() {}
public Boat(boolean motorized) {
super(45);
this.motorized = motorized;
}
@property
private boolean motorized = true;
public boolean isMotorized() {return motorized;}
public void setMotorized(boolean motorized) {this.motorized = motorized;}
}
The classes are loaded into Hibernate using the snippet below.
Class[] classes = {
Person.class,
Vehicle.class, Car.class, Boat.class
};
public void test_hsqldb() throws HibernateException {
Configuration cfg = new Configuration();
for (Class c : classes) { cfg.addAnnotatedClass(c); }
cfg.setProperties( createHsqlProps() );
new SchemaExport(cfg).create(true, true);
SessionFactory sf = cfg.buildSessionFactory();
HibernateTemplate h = new HibernateTemplate(sf);
Person p = new Person("Sham Poo");
h.save(p);
final Long id = p.getId();
Person q = (Person) h.get(Person.class, id);
h.execute(new HibernateCallback() {
public Object doInHibernate(Session s) throws HibernateException, SQLException {
Person w = (Person) s.get(Person.class, id);
w.addVehicle( new Car("ABC123") );
w.addVehicle( new Boat(true) );
s.update(w);
return null;
}
});
List<Person> persons = (List<Person>) h.find("from "+Person.class.getName());
sf.close();
assertEquals("persons.size", 1, persons.size());
q = persons.get(0);
assertEquals("q.vehicles().size", 2, q.getVehicles().size());
}
protected Properties createHsqlProps() {
Properties props = new Properties();
props.setProperty(Environment.DIALECT , HSQLDialect.class.getName());
props.setProperty(Environment.DRIVER , org.hsqldb.jdbcDriver.class.getName());
props.setProperty(Environment.URL , "jdbc:hsqldb:mem:foobar");
props.setProperty(Environment.USER , "sa");
props.setProperty(Environment.PASS , "");
return props;
}
Implementing all mapping XML tags, is in progress. The current "vertical slice" release do not define all tags yet. The tag set implemented so far is listed below.
| @Hibern8 Tag | Hibernate XML Tag | Description |
|---|---|---|
| clazz | class | Annotates an entity class. |
| id | id | ID property. |
| property | property | Ordinary property. |
| component | component | Identifies a component. |
| subclass | subclass | Defines an entity subclass. |
| discriminator | discriminator | Identifies a subclass. |
| mapping | hibernate-mapping | Root node a mapping XML. Generated by default, but can be used to define specific attributes. |
| manyToOne | many-to-one | The 'many' side of an association. |
| set | set | The 'one' side of an one2many association |
| parent | parent | Used to mark a property, inside a component class, as the link back to its parent. |
| param | param | Used as a <param name="key">value</param> node. |