Skip to end of metadata
Go to start of metadata

Originally LAMS was written using Hibernate xml files (.hbm.xml) but with LDEV-4696 we switched to Hibernate 5.3 and annotated the Java beans used for Hibernate. These notes will help you if you are fixing bugs related to these changes or when creating new tools.

These instructions were written for Wildfly 14.

Do not use Legacy-style ? query parameters

This are not supported by Hibernate 5.3. At runtime, when the query is executed you will get the error "Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead".

To fix:

  1. If the calling method uses doFind(), then leave the HQL code using "?". The doFind method will change the "?" to P1, P2, P3, etc and generate matching named parameters P1, P2, P3, etc and fix it for you.
  2. If not using doFind(), change the HQL/SQL to use meaningful named parameters.

Use Consistent Java Package Structure

Suggested package structure:
org.lamsfoundation.lams.tool.<toolname> : Constants, applicationContent.xml
org.lamsfoundation.lams.tool.<toolname>.dao
org.lamsfoundation.lams.tool.<toolname>.dao.hibernate
org.lamsfoundation.lams.tool.<toolname>.dbupdates
org.lamsfoundation.lams.tool.<toolname>.dto
org.lamsfoundation.lams.tool.<toolname>.model : Hibernate related POJOs in a "model" package.
org.lamsfoundation.lams.tool.<toolname>.service
org.lamsfoundation.lams.tool.<toolname>.util
org.lamsfoundation.lams.tool.<toolname>.web.
org.lamsfoundation.lams.tool.<toolname>.web.controller
org.lamsfoundation.lams.tool.<toolname>.web.form

Note: Remove any service proxy class if they exists.

Using a model directory is very important for tools - the Hibernate Annotations scan in commonContext.xml is set up for "org.lamsfoundation.lams.tool.**.model" so if your annotated classes are in a different package, they will not be mapped with Hibernate.

Project Classpath

Ensure the following path is on the project build classpath (or the newer version that matches whatever is in your Wildfly directories).

WILDFLY_HOME/modules/system/layers/base/javax/persistence/api/main/javax.persistence-api-2.2.jar

Note: hibernate-core-5.3.6.Final.jar is included in the build via lams_build/lib and in 3rd Party Libraries at the moment so we are doubling up, but once we've updated all the projects we can drop it from there and just use the project classpaths to access the jars directly from Wildfly.

Annotation of Hibernate Objects

When you are importing the classes needed for the annotations, select the javax.persistence.* ones, not the Hibernate ones - apart from cascade="save-update" (see later). You will need the use the Hibernate GenericGenerator import (and this is why you need hibernate-core-5.3.6.Final.jar in your build path).

If you find any 2nd level cache tags ( <cache usage="transactional" />) just ignore them. Do not add cache annotations. We will revisit the caching when we have it enabled and will do it in the appropriate JPA way then.

Entity and Table Annotations For Class

@Entity
@Table(name = "lams_learning_activity")

If the object is the root of an inheritance hierachy then add

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "learning_activity_type_id", discriminatorType = DiscriminatorType.INTEGER)

Then in the subclass add the Discriminator

@DiscriminatorValue("2")

Id field

@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long uid;

Column Annotation

Add a @Column annotations for all other persistent fields. Just use name and columnDefinition options. Name is required (and should be omitted) if the name of the field is identical to the name of the column. If you want to note anything special about the column use the columnDefinition option. The column definition and the other options (length, type, etc) are only for DDL so no point meticulously recording them all when we maintain the database manually - we just risk the definitions in the code getting out of sync with the database.

If the field is the DiscriminatorColumn (see above) then use:

@Column(name = "learning_activity_type_id", insertable = false, updatable = false)

Transient Annotation

Every non-static and non-transient property are assumed persistent so any temporary values (and some POJOs are used in the web layer) must be marked as @Transient.

Relationship Annotation

https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/

If a collection is sorted, you need to use @OrderBy

In a one-to-many like content to session we have:
Content side:


        <set
            name="nbSessions"
            lazy="true"
            inverse="true"
            cascade="all-delete-orphan"
            sort="unsorted"
        >
            <key
                column="nb_content_uid"
            >
            </key>
            <one-to-many
                  class="org.lamsfoundation.lams.tool.noticeboard.NoticeboardSession"
            />
        </set>

Becomes

   @OneToMany(mappedBy = "nbContent",
        cascade = CascadeType.ALL,
        orphanRemoval = true)
    private Set<NoticeboardSession> nbSessions = new HashSet<NoticeboardSession>();

Session side:

       <many-to-one
            name="nbContent"
            class="org.lamsfoundation.lams.tool.noticeboard.NoticeboardContent"
            cascade="none"
           
            update="true"
            insert="true"
        >
            <column
                name="nb_content_uid"
            />
        </many-to-one>

Becomes

    @ManyToOne
    @JoinColumn(name = "nb_content_uid")
    private NoticeboardContent nbContent;

If you find a one-to-many with inverse = "false" (ie control is from the one side, not the many side) this may be implementable using a join column, rather than a mappedBy. For example, in Forum there is a link from the Message table back to itself, using the "authored_parent_uid" field.

    @OneToMany(fetch = FetchType.EAGER) 
    @JoinColumn(name = "authored_parent_uid")
    private Set<Message> sessionClones;
  • You do not need to specify "fetch = FetchType.LAZY" in @OneToMany relationship as it is the default, but you will need to define "fetch = FetchType.EAGER" if you come across a mapping with lazy="false".
  • cascade="save-update" needs to be converted to Hibernate CascadeType.SAVE_UPDATE should be used instead of the JPA cascade.

use-in-equals and use-in-tostring

If you find any <meta attribute="use-in-equals">true</meta>, <meta attribute="use-in-tostring">true</meta> entries and the class does not have a toString() or equals() method you can write methods based on these attributes. They are not converted to annotations.

Future Bean Validation

If we want to use length, not null, etc validation at runtime we need to use HibernateValidator, which implements Bean Validation. Looking at all the examples and having a go at getting it running, Spring MVC is set up to use the Bean Validation, but the Hibernate ORM layer is not. Therefore we will leave this for now and revisit it as needed in the web layer.

  • No labels