Behavior

In this section, we look at those annotations that affect the behavior of the entity.

@Cache

When you enable second-level cache, you can mark an entity as cacheable. In JPA, you can't specify a strategy, and the only strategy that is supported is READ_ONLY; refer to @Cacheable in JPA docs. Hibernate offers the capability to specify other strategies, besides READ_ONLY. We will see more on this in Chapter 5, Hibernate Cache.

@Access

The @Access annotation is a JPA annotation and is used to specify how to access a class field. By default, fields are accessed directly, typically using reflection, depending on the visibility of a field. But in some cases, you may want to perform additional work in the property accessor methods, such as getters and setters. In that case, you can instruct JPA (in this case, Hibernate) to not access the fields directly and use the accessor methods instead. (Refer to the access method of field and property in the JavaBeans™ specifications.)

You can annotate the class field or the class itself. If you use this annotation at the class level, it will apply to all the fields, including the ID. For this reason, you can't use generated IDs, and you will have to set the ID for this entity manually, which might be desirable in some cases. If that's not desirable, you should simply annotate the fields whose access type should be set accordingly.

Here is a sample listing that shows the @Access annotation used at the class level. Note, in this case, the @Id annotation must be applied to the getter method of ID:

@Entity
@Access(AccessType.PROPERTY)
public class Course {
  private long id;
  private String title;

  @Id
  public long getId() {
    return id;
  }
  // Other getters and setters
}

The following sample code shows the same annotation applied to select fields:

@Entity
public class Course {
  @Id
  @GeneratedValue
  private long id;
  
  @Access(AccessType.PROPERTY)
  private String title;

  // getters and setters as usual
}

@Cascade

This @Cascade annotation is similar to the cascade type you would define for one-to-one or one-to-many associations in JPA. However, it provides cascade behavior for query operations that are not supported in JPA, such as native delete, lock, detach, replicate, or SaveOrUpdate. Using @Cascade, you can propagate the native operations to the associated entities.

@CreationTimestamp and @UpdateTimestamp

The @CreationTimestamp and @UpdateTimestamp Hibernate annotations are used for fields whose types can be used as timestamps, for example, Date, Calendar, and Timestamp. Both annotations use the current date/time provided by the runtime JVM. The creation timestamp is set when you call session.save() or session.saveOrUpdate(), but when this is an existing object, the creation timestamp is not set at update time.

One interesting feature is that when you use these annotations, you don't need accessor methods such as getters and setters. (Unless you wish to fetch the values and display to the user, such as in a report, in which case you only need a getter.) Hibernate sets the values for you both in Java and SQL.

@Filter and @FilterDef

The @Filter and @FilterDef Hibernate annotations allow you to dynamically apply filters to your entity, and because you can change the behavior of an entity dynamically, it is listed in this section. But in reality, this annotation modifies the SQL at runtime.

At first, you define the filter typically applied to collections, and then you can enable the filter when you open a new session. Next, you can set the parameters of the filters dynamically.

Consider the following entities:

@Entity
public class Student {
  @Id
  @GeneratedValue
  private long id;
  private String name;
  private char gender;
  // getters and setters
}

@Entity
@FilterDef(name="genderFilter", 
    parameters=@ParamDef(name="gender", type="character"))
public class Course {
  @Id
  @GeneratedValue
  private long id;
  private String title;
  
  @OneToMany(cascade=CascadeType.ALL)
  @Filter (
    name="genderFilter",
    condition="gender = :gender"
  )
  private Set<Student> students = new HashSet<Student>();
  // getters and setters
}

The Course entity includes a filter definition to associate with students based on their gender. By default, all filters are disabled. So, all students associated with this course will be returned. However, you can activate the filters as shown in the following listing:

  Filter filter = session.enableFilter("genderFilter");
  filter.setParameter("gender", 'F');
  Course course = (Course) session.get(Course.class, courseId);
  Set<Student> students = course.getStudents();

You can enable filters only on open sessions and after a transaction has begun.

@Immutable

The @Immutatble Hibernate annotation allows you to protect your entities from accidental updates. When you mark an entity as immutable, Hibernate will quietly ignore any update to the entity without any errors or exceptions. But if you apply this annotation to a collection and try to add or remove from the collection, Hibernate will throw an exception.

This is a useful annotation to protect changes to the database, for example, for reference or static data. Alternatively, you can declare two flavors of the same entity, one mutable and another immutable, where the mutable flavor is used in the application's administration business logic and the immutable used for non-privileged users.

@Loader

The @Loader Hibernate annotation lets you override the SQL query used to fetch the data. It uses a named query to load the data (refer to @NamedQuery). We will see examples of this in the next chapter when we discuss fetching.

@NotFound

Most of the time, we develop new applications that replace an old one. But in most cases, the database schema and data remain unchanged because the effort to migrate the data to a new schema is costly and very risky. For this reason, you often have to deal with data integrity issues, for example, when you have to deal with a database that lacks proper referential constraints.

Hibernate offers this annotation to handle cases where you except an association, but it doesn't exist in the database. Note that this will not work for cases where Hibernate joins the two tables to fetch the two entities, because in that case the SQL join will return no results. In other words, this is useful when you use lazy fetch, or SELECT strategy.

To demonstrate this, consider the following relations:

@NotFound

Notice that there is no row for a course whose ID is 1. So the referential integrity of this data has been compromised. If you don't use the @NotFound annotation, Hibernate will throw an exception by default when you try to fetch an enrolment entity from the database. You can suppress this exception as shown in the code listing:

@Entity
public class Course {
  @Id
  @GeneratedValue
  private long id;
  private String title;
  
  @OneToMany(mappedBy="course")
  private Set<StudentEnrolment> students = new HashSet<StudentEnrolment>();
  // getters and setters
}

@Entity
public class StudentEnrolment {

  @Id
  @GeneratedValue
  @Column(name="student_id")
  private long studentId;
  
  @ManyToOne
  @NotFound(action=NotFoundAction.IGNORE)
  Course course;
  // getters and setters
}

The default value for NotFoundAction is EXCEPTION, which causes an exception to be thrown. This is recommended by the Hibernate team, but clearly it depends on your use case.

@SortComparator and @SortNatural

The @SortComparator and @SortNatural annotations only exist in Hibernate and they are used to sort a collection in memory, as opposed to adding the order by clause to the SQL. You may not use both annotations together; it's one or the other. Here, we show an example of how to use @SortComparator:

@Entity
public class Team {
  @Id
  @GeneratedValue
  private long id;
  
  @OneToMany(cascade=CascadeType.ALL)
  @SortComparator(value=PersonComparator.class)
  private Set<Person> persons = new HashSet<Person>();
  // getters and setters
}

@Entity
public class Person {
  @Id
  @GeneratedValue
  private long id;
  private String firstname;
  private String lastname;
  // getters and setters
}

public class PersonComparator implements Comparator<Person> {
  @Override
  public int compare(Person p1, Person p2) {
    if (p1.getLastname().equals(p2.getLastname())) {
    return p1.getFirstname().compareTo(p2.getFirstname());
    }
    return p1.getLastname().compareTo(p2.getLastname());
  }
}

The order of the persons without the sort will be like this:

Person [id=3, firstname=Adam, lastname=Smith]
Person [id=2, firstname=David, lastname=Smith]
Person [id=4, firstname=David, lastname=Brown]

However, if you add the Sort annotation (with the comparator), the order will be as follows:

Person [id=4, firstname=David, lastname=Brown]
Person [id=3, firstname=Adam, lastname=Smith]
Person [id=2, firstname=David, lastname=Smith]

Lastly, note that for the preceding example, I used HashSet. This works only if you are just reading the related entities and wish to sort them in memory. On the other hand, if you wish to use the same entity to write to the database, you will need to use a sorted set type for the collection.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.7.240