Entities for the project

If we want to work with a relational database we need our entities which are stored in and loaded from the database. Entities are Java objects which will be mapped to a line in a relational database table.

In this post I’ll introduce the needed entities for the variations project.

There are many methods to work with entities and relational databases. I prefer the one Java object for each database table. However there are some other requirements which let you change your mind. For example you have an enormous big table with a lots of fields. In this case you should split the contents into more than one Java class — and embed them in each other. Let’s agree that I’ll split the classes in this manner up if they grow too big. Until that I’ll just briefly mention how it works.

As you could see in the previous articles I’ve added some tables for future entities to the project. Now I’ve added the entity classes to the project too.

Le’s examine the Driver class (the other classes look the same only holding other variables):

@Entity
public class Driver
{
    @Id
    @GeneratedValue
    private Long id;

    @Column(name="NAME", nullable=false, length=255)
    private String driverName;

    @Column(name="DRIVING_LICENCE_NUMBER", nullable=false, length=30)
    private String driversLicenceNumber;

    @Column(name="DRIVING_LICENCE_CATEGORY", nullable=false, length=30)
    private String driversLicenceCategory;

    @Column(name="DRIVING_LICENCE_EXPIRES", nullable=false)
    @Temporal(TemporalType.DATE)
    private Date driversLicenceExpiration;
}

The class itself is annotated as an Entity. It tells the persistence containers (Hibernate or any other) that this class functions as an Entity in the database.

The fields are annotated too. The difference is at the ID which is marked with the @Id annotation to show that this field is the primary key. The @GeneratedValue indicates that this field will be set upon commit from the underlying management container. If the @Column annotation is not present for a field marked as ID then it’s assumed that the field’s name equals the column’s name in the database.

The other fields have their @Column annotation where you can set the name, nullability, length, precision and some other properties of the column. By convention I always add the name property to the @Column annotations even if the column’s name is the same as the variable’s name.

The @Temporal annotation provides mapping for java.util.Date and java.util.Calendar classes: you can specify how you want to store them in the database with the javax.persistence.TemporalType enumeration. I took date because we’re not interested in time parts of the expiration date.

If you look at the ShippingNote class you’ll see that it contains only the IDs of the referenced tables (such as Vehicle, Driver, Route). A better way would be to include the classes themselves and create a join of them.

A bit of code

To see the entities working we need to trigger saving from the application too. For this I’ve added some code in the App class’ main method:


DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

Driver driver = new Driver("Gabor Hajba", "12345", "B", dateFormat.parse("2016-05-22"));

service.saveData(driver);

System.out.println(driver.getId());

This code saves a new Driver entity in the database and prints its newly generated ID to the console. After saving the load query will return this entity in the next run of the application (if you run the code more times after, you’ll end up having the same driver in the database many times — however the application will only load the one with ID=1).

generating the ids

One important point in working with entities is if you want to generate your IDs automatically or not.

I’ve added to each entity the @GeneratedValue to the ID field however this does not work alone.

@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="driver_sequence")

If you run this example you’ll get an error that Hibernate does not know about the generator named “driver_sequnece”.

Caused by: org.hibernate.AnnotationException: Unknown Id.generator: driver_sequence
    at org.hibernate.cfg.BinderHelper.makeIdGenerator(BinderHelper.java:640)
    at org.hibernate.cfg.AnnotationBinder.processId(AnnotationBinder.java:2359)
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2265)
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:963)
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:796)
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3790)
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3744)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1410)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1928)
    at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.buildSessionFactory(LocalSessionFactoryBuilder.java:343)
    at org.springframework.orm.hibernate4.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:431)
    at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:416)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
... 12 more

The solution for this is to specify the sequence generator explicitly to let Hibernate know which sequence to use. The whole ID definition for the Driver class is as follows:

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="<strong>driver_sequence</strong>")
@SequenceGenerator(name="<strong>driver_sequence</strong>", sequenceName="driver_seq", allocationSize=1)
private Long id;

Here the “driver_seq” is the sequence to get the IDs from. The allocationSize tells Hibernate how many sequences he should allocate. I’ve set this to 1 because the default is 50 to improve performance. If you leave the allocationSize you’ll end up with getting IDs for each run which are multiples of 50. This is because the application asks the database for the next value of the referenced sequence, multiplies it by 50 and starts to increment this number until it reaches the 50 it has to give away. This works until nodes use the same allocation size — if one one would use for the same sequence 25 you’ll end up in an exception violating the unique primary key constraint.

However if you try to run the code you’ll get an exception that the sequence does not exist:

Caused by: org.h2.jdbc.JdbcSQLException: Sequence "DRIVER_SEQ" not found; SQL statement:
    call next value for driver_seq [90036-175]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:332)
    at org.h2.message.DbException.get(DbException.java:172)
    at org.h2.message.DbException.get(DbException.java:149)
    at org.h2.command.Parser.readSequence(Parser.java:5118)
    at org.h2.command.Parser.readTerm(Parser.java:2640)
    at org.h2.command.Parser.readFactor(Parser.java:2178)
    at org.h2.command.Parser.readSum(Parser.java:2165)
    at org.h2.command.Parser.readConcat(Parser.java:2138)
    at org.h2.command.Parser.readCondition(Parser.java:2003)
    at org.h2.command.Parser.readAnd(Parser.java:1976)
    at org.h2.command.Parser.readExpression(Parser.java:1968)
    at org.h2.command.Parser.parseCall(Parser.java:4213)
    at org.h2.command.Parser.parsePrepared(Parser.java:345)
    at org.h2.command.Parser.parse(Parser.java:298)
    at org.h2.command.Parser.parse(Parser.java:270)
    at org.h2.command.Parser.prepareCommand(Parser.java:235)
    at org.h2.engine.Session.prepareLocal(Session.java:436)
    at org.h2.engine.Session.prepareCommand(Session.java:379)
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1138)
    at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:70)
    at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:267)
    at com.mchange.v2.c3p0.impl.NewProxyConnection.prepareStatement(NewProxyConnection.java:162)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$1.doPrepare(StatementPreparerImpl.java:103)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)
    ... 30 more

This is obvious because you need the sequence in your database too — with the same name as you reference to it. For this a new database change is needed — which we can do with Liquibase. The code is simple, Liquibase has its own tag for creating sequences:

<changeSet id="sequences" author="ghajba">
    <createSequence incrementBy="1" startValue="1" sequenceName="driver_seq" />
    <createSequence incrementBy="1" startValue="1" sequenceName="route_seq" />
    <createSequence incrementBy="1" startValue="1" sequenceName="shipping_note_seq" />
    <createSequence incrementBy="1" startValue="1" sequenceName="user_seq" />
    <createSequence incrementBy="1" startValue="1" sequenceName="vehicle_seq" />
</changeSet>

As you can see I’ve added all sequences for the classes. It is a good practice to name your sequences as your tables with the “_seq” suffix. Or more general said it is a good practice to have common practice to name your tables and sequences to identify the togetherness easily.

The change set needs an entry in the releasechanges.xml too:

<include file="sequences.xml" relativeToChangelogFile="true"/>

After this you can execute the application.

Default constructor

Hibernate needs a default (no argument) constructor if you query entities. Otherwise you’ll get an exception:

Caused by: org.hibernate.InstantiationException: No default constructor for entity:  : biz.hahamo.dev.variations.model.Driver
    at org.hibernate.tuple.PojoInstantiator.instantiate(PojoInstantiator.java:120)
    at org.hibernate.tuple.PojoInstantiator.instantiate(PojoInstantiator.java:136)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.instantiate(AbstractEntityTuplizer.java:737)
    at org.hibernate.persister.entity.AbstractEntityPersister.instantiate(AbstractEntityPersister.java:4761)
    at org.hibernate.internal.SessionImpl.instantiate(SessionImpl.java:1391)
    at org.hibernate.internal.SessionImpl.instantiate(SessionImpl.java:1379)
    at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1618)
    at org.hibernate.loader.Loader.getRow(Loader.java:1514)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:725)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:952)
    at org.hibernate.loader.Loader.doQuery(Loader.java:920)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354)
    at org.hibernate.loader.Loader.doList(Loader.java:2553)
    at org.hibernate.loader.Loader.doList(Loader.java:2539)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2369)
    at org.hibernate.loader.Loader.list(Loader.java:2364)
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:496)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:387)
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:231)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1264)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103)
    at org.hibernate.internal.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:966)
    at biz.hahamo.dev.variations.controller.repository.internal.hibernate.GenericHibernateRepository$1.doInHibernate(GenericHibernateRepository.java:73)
    at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:340)
    ... 14 more

This is because Hibernate creates entities which are loaded by Reflection as Class<T>.newInstance(). If you do not have a default constructor because you’ve defined one with parameters yourself (note that if you do not define any constructors Java treats your class as having a default constructor) you need one. If you want however to protect your class to get instantiated without any required parameters you can add the default constructor as private or protected. With protected you do not get any warnings at compiling and in your IDE.

wrap-up

In this article I’ve shown the entities (or at least the Driver but the others work the same) and how to make Hibernate to generate the ID for you.

As always the code can be found at the Variations GitHub repository.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s