Hibernate collection types and Seam

By default Seam maps all of your Hibernate collections as Sets. This is also the most common Hibernate collection type. Keep in mind though that this may not always be the collection type you need.

The other day I was working on a project and I was adding objects to my collection and displaying them in the view. I noticed however in the view that they weren’t displaying in the order that they were added. After scratching my head for a while I finally realized that it was because they were being added to a Set which doesn’t preserve the order of the elements. To resolve the problem I simply changed my collection type from Set and List in my Entity class.

Here is a quick rundown of the collection types supported by Hibernate. For more detailed information check the Hibernate documentation.

  • A java.util.Set is mapped with a <set> element. Initialize the collection with a java.util.HashSet.
    • The order of its elements aren’t preserved.
    • Duplicate elements aren’t allowed.
    • This is the most common persistent collection and the default collection type for Seam.

  • A java.util.SortedSet is mapped with a <set> element. Initialize the collection with a java.util.TreeSet.
    • The sort attribute can be set to either a comparator or natural ordering for in-memory sort.

  • A java.util.List is mapped with a <list> element. Initialize the collection with a java.util.ArrayList.
    • The position of each element is preserved with an additional index column in the collection table.

  • A java.util.Collection is mapped with a <bag> or <idbag> element. Initialize the collection with a java.util.ArrayList.
    • Allows possible duplicates.
    • The order of the elements aren’t preserved.
    • Uses a list internally but ignores the index of the elements.

  • A java.util.Map is mapped with a <map> element. Initialize the collection with a java.util.HashMap.
    • Preserves key and value pairs.

  • A java.util.SortedMap is mapped with a <map> element. Initialize the collection with a java.util.TreeMap.
    • The sort attribute can be set to either a comparator or natural ordering for in-memory sort.

  • Arrays are supported as well by Hibernate however they are rarely used and aren’t supported by the JPA standard.


Soft deletes using Hibernate annotations.

blog-exampleI am currently working on a Seam application that has a need for soft deletes in the database. To the right you can see a snippet of my database diagram which contains a CUSTOMER and APP_USER table. This is just a straight forward one to many relationship but the important thing to note though is the “DELETED” field in each table. This is the field that will be used to track the soft delete. If the field contains a ‘1’ the record has been deleted and if it contains a ‘0’  the record hasn’t been deleted.

Before ORMs like Hibernate I would have had to track and set this flag myself using SQL. It wouldn’t be super hard to do but who wants to write a bunch of boilerplate code just to keep track of whether or not a record has been deleted. This is where Hibernate and annotations comes to the rescue.

Below are the 2 Entity classes that were generated by Hibernate using seamgen. I have omitted parts of the code for clarity.

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
31
32
33
34
35
36
37
//Package name...

//Imports...

@Entity
@Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
@SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
@Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
private long id;
private Billing billing;
private String name;
private String address;
private String zipCode;
private String city;
private String state;
private String notes;
private char enabled;
private char deleted;
private Set appUsers = new HashSet(0);

//Constructors...

//Getters and Setters...

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "customer")
//Filter added to retrieve only records that have not been soft deleted.
@Where(clause="deleted <> '1'")
public Set getAppUsers() {
return this.appUsers;
}
public void setAppUsers(Set appUsers) {
this.appUsers = appUsers;
}
}
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
31
//Package name...

//Imports...

@Entity
@Table(name = "APP_USER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
@SQLDelete(sql="UPDATE app_user SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
@Where(clause="deleted <> '1'")
public class AppUser implements java.io.Serializable {
private long id;
private Customer customer;
private AppRole appRole;
private char enabled;
private String username;
private String appPassword;
private Date expirationDate;
private String firstName;
private String lastName;
private String email;
private String phone;
private String fax;
private char deleted;
private Set
persons = new HashSet(0);

//Constructors...

//Getters and Setters...
}

The following 2 steps is all that I had to do to implement the soft delete.

  1. Added the @SQLDelete annotation which overrides the default Hibernate delete for that entity.
  2. Added the @Where annotation to filter the queries and only return records that haven’t been soft deleted. Notice also that in the CUSTOMER class I added an @Where to the appUsers collection. This is needed to fetch only the appUsers for that Customer that have not been soft deleted.

Viola! Now anytime you delete those entities it will set the “DELETED” field to ‘1’ and when you query those entities it will only return records that contain a ‘0’ in the “DELETED” field.

Hard to believe but that is all there is to implementing soft deletes using Hibernate annotations.