Chapter 5. Domain Model Fundamentals

The domain model is the foundation upon which a persistence tier is constructed. Each domain class defines the properties to be persisted to the database, as well as the relationships between one class and another. This rich object-oriented structure is not easily translated to the relational world of databases. Hibernate provides the required mechanism to help address this impedance mismatch between these two realms.

Mapping is the process through which you provide hints to Hibernate regarding how the properties and references in your domain classes are translated to tables, fields, and associations in your database. When Hibernate first appeared on the scene, developers used XML (called .hbm.xml files) to specify a domain model's mapping rules. With the release of the JPA specification came a series of annotations that can be applied to your domain classes, providing similar types of hints to the XML mapping files.

Hibernate's strength is the ease with which developers can begin building a persistence tier. The first step is usually to define your domain model using simple JavaBeans (or POJOs). In the previous chapter, we introduced several core classes that compose the root of our application's domain model. In this chapter, we will build on this foundation, introducing some additional classes.

Understanding Associations

In Chapter 4, we introduced our art gallery domain model and created the Person entity. We mentioned that the ArtEntity class will represent artwork, images, and photos in our gallery application. We also said that our domain model will include a Comment class, which will represent an individual comment about a particular ArtEntity.

An ArtEntity will naturally contain multiple comments to allow for an unlimited number of site visitors to add their own comments about the particular piece of art they are viewing. Although an ArtEntity may contain many Comment instances, a given Comment can reference only a single ArtEntity, as typically a comment is intended to relate to a particular piece of content within the gallery application. The association between an ArtEntity and its Comment instances is best described as a one-to-many relationship. Inversely, the relationship between a Comment and its associated ArtEntity is known as a many-to-one association. Because each entity is able to reference the other, the association is considered to be bidirectional. If one entity is able to reference another entity, but the inverse is not true, this is considered a unidirectional association.

Whether you should use unidirectional or bidirectional associations depends on your application. However, if you don't have a specific requirement to use bidirectional associations, it can be easier to stick with a unidirectional approach, as bidirectional associations can require circular references and may end up complicating marshaling or serialization implementations.

It's always important to consider the way in which the domain model and its relationships will be translated into a database schema, even when ORM abstractions often handle these details for us. The ArtEntity and Comment association will require two tables: an Art_Entity table and a Comment table. An ArtEntity instance will then be associated with a Comment through a foreign key reference to the Art_Entity in the Comment table, as illustrated in Figure 5-1.

The relationship between the ArtEntity and Comment tables

Figure 5.1. The relationship between the ArtEntity and Comment tables

Our gallery application will also require a Category class to represent a category into which a particular ArtEntity may be placed (to help organize artwork and photos into logical groups). Each Category may contain more than one ArtEntity instance. Similarly, each ArtEntity may be placed into multiple Category entities. This type of association is normally referred to as many-to-many. The many-to-many association is a bit more complicated than the one-to-many relationship. The best way to model this type of relationship in the database is to use a join table. A join table simply contains foreign keys from the two related tables, allowing rows in the two tables to be associated with each other. Figure 5-2 illustrates this relationship.

The relationship between the ArtEntity and Category tables

Figure 5.2. The relationship between the ArtEntity and Category tables

Although it is important to have a clear understanding of your domain model's table structure, Hibernate can take care of creating these database-specific details for you. Instead, you need to focus on the definition of the classes and the way they relate to each other from an object-oriented standpoint.

Developers have different philosophies on the best way to go about defining a Hibernate domain model. Some developers believe it is best to define a database schema first, and then create the classes to match the database structure. Obviously, there is no wrong way (provided your application works reliably) to go about this process. However, in our experience, we have achieved the best results by defining the Hibernate mappings first, allowing us to consider the Java classes and the database table structure in tandem.

With JDK 1.5 and Hibernate 3, the definition of Hibernate mapping files in XML is no longer necessary. Of course, you are welcome to continue following this more verbose methodology, and for many developers, externalizing the specifics of the database mapping is very much a good thing. However, it is hard to argue the fact that using Hibernate's new annotation support is easier and far less verbose. But using annotations isn't your only (nor necessarily best) option.

Building the Domain Model

We've already described a few of our sample application's core entities, along with their corresponding associations. Now that we've considered how these entities will be represented in the database, let's start building our Java classes. Let's first define the Comment class:

@Entity
public class Comment implements Serializable {

    private Long id;
    private String comment;
    private ArtEntity commentedArt;
    private Date commentDate;
    private String firstName;
    private String lastName;;
    private Integer version;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne
    public ArtEntity getCommentedArt() {
        return commentedArt;
    }

    public void setCommentedArt(ArtEntity commentedArt) {
        this.commentedArt = commentedArt;
    }
@Temporal(TemporalType.TIMESTAMP)
    public Date getCommentDate() {
        return commentDate;
    }

    public void setCommentDate(Date commentDate) {
        this.commentDate = commentDate;
    }

    @Version
    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

}

Next, let's define the ArtEntity class:

@Entity
public class ArtEntity implements Serializable {

    private Long id;
    private String title;
    private String subTitle;
    private Date uploadedDate;
    private Date displayDate;
    private Integer width;
    private Integer height;
    private String media;
    private String description;
    private String caption;
    private String imagePath;
    private Integer version;
    private Set<Category> categories = new HashSet();
    private Set<Comment> comments = new HashSet();

    public ArtEntity() {
    }

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }
public void setId(Long id) {
        this.id = id;
    }

    @Version
    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    @ManyToMany(mappedBy = "artEntities")
    public Set<Category> getCategories() {
        return categories;
    }

    public void setCategories(Set<Category> categories){
        this.categories = categories;
    }

    @OneToMany
    public Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }

    public boolean addCommentToArt(Comment comment) {
        comment.setCommentedArt(this);
        return this.getComments().add(comment);
    }

}

Note

For simplicity, the domain objects in these code listings only implement Serializable. However, as we demonstrated in the previous chapter, our GenericDao assumes that each domain entity implements our DomainObject marker interface. Although our code samples follow this approach, we've kept these listings a bit simpler for illustrative purposes.

We've omitted some of the redundant getters and setters to conserve space. However, you'll immediately recognize that we're essentially defining a JavaBean or POJO. There is no reference to Hibernate dependencies, and no parent class from which to extend.

We have defined the properties that we need to persist in the database along with their respective getters and setters. As well as the appropriate getters and setters, we have also added an addCommentToArt(Comment comment) method. This is a convenience method for bidirectional associations, since it is important that references are set on both sides of the association. In the addCommentToArt(Comment comment) method, we ensure that the specified comment parameter is added to the ArtEntity's comment collection and that the comment's commentedArt property properly references the ArtEntity instance. We strongly recommend creating this type of "association management" method on one side of the relationship to ensure that both sides of a bidirectional relationship are properly set.

Our Comment domain entity also has the @Many ToOne annotation. This tells Hibernate that the commentedArt property will have a many-to-one association to the ArtEntity table. From a database perspective, specifying a @ManyToOne annotation on the Comment field will add a foreign key field on our Comment table to the ArtEntity table. This also demonstrates some of the advantages of using Hibernate to architect both your domain model and your database schema. If Hibernate is used to generate your schema, it will also create foreign key constraints for your associations to ensure the referential integrity of your database is not compromised.

Convention over Configuration

The simplicity of Hibernate's annotation support stems from using sensible defaults, as well as Hibernate's ability to infer associations and database field types by considering the Java type of each JavaBean property. When mappings are defined in XML, we must explicitly delineate the details of each property and association. With Hibernate, because annotations are embedded into code, we have the benefit of drawing hints from the code itself, which dramatically simplifies configuration efforts.

As you learned in Chapter 4, the key annotation for Hibernate persistence is @Entity. This annotation tells Hibernate that we intend to persist this class. If we were following the XML mapping approach, we would then need to define each field explicitly in the hbm.xml mapping file. With Hibernate annotations, it is necessary to define only the details that don't conform to Hibernate's default behavior.

Hibernate will look at each property's Java type and its name, and use this metadata to define a column's field type and field name, respectively. The default behavior is to assume that all POJO properties are persistable, but you can also specify this behavior explicitly by using the @Basic annotation. Using @Basic also provides you with a way to customize various persistence-related aspects, such as whether a particular property should be lazily loaded. If you don't want certain fields persisted, you need to specify that these properties are transient using the @Transient annotation.

Note

Hibernate offers control over fetching associations, allowing related entities to be lazily loaded —that is, only when needed, rather than when the originating object is loaded from the database. This has dramatic performance benefits (if used properly), but can also degrade performance if you're not careful. We'll be covering lazy loading in more detail in Chapter 9.

In addition to the @Transient and @Basic annotations, you can also use the @Temporal annotation for controlling the way date or time-based properties are mapped to the database. In our Comment class, we specify the following to declare that a Date property be persisted in the database as a timestamp:

@Temporal(TemporalType.TIMESTAMP)
public Date getCreatedDate() {
    return this.createdDate;
}

This concept of sensible defaults, or convention over configuration, really reduces the amount of coding required to get a domain model up and running. And Hibernate's annotation support provides ample flexibility to override any of the default behavior, should you be so inclined. For instance, if we wanted to define a table name for our Comment class that is different from the Java class name, we could accomplish this feat by using the @Table annotation:

@Table(name = "HOGWASH")
class Comment {
    . . . (Methods Omitted)
}

Similarly, we can require that the comment property maps to the column commentText by using the @Column annotation:

@Column(name = "commentText")
public String getComment() {
    return this.commentText;
}

This level of customization is very useful, but most of the time is unnecessary and redundant (unless you are mapping your domain model to a legacy database).

Note

You should have a very good reason before you override any of Hibernate's default behavior. If you feel the need to map a Java property to a column of a different name, you may want to reconsider your naming conventions. Now, it's not wrong to have discrepancies between column names and Java property names, but simplicity of configuration is very important, and we encourage you to limit the overriding of default behavior whenever possible. After all, less code equals less maintenance.

Managing Entity Identifiers

Hibernate annotation support does require that you define a primary key and all of your JavaBean's associations. In our Comment class, we have added the @Id annotation above the getId() method. This annotation tells Hibernate that the id property of our Comment class is the identifier (or primary key) for our Comment entity.

Below the @Id annotation is the @GeneratedValue annotation, which specifies the way in which a given instance's identifier will be created. The default is AUTO, and in our example, this is the identifier-generation strategy Hibernate will use (since we haven't defined a strategy at all). AUTO will look at the underlying database to make the decision as to how identifiers should be created. The options are to use a sequence, an identity column, or to use a special table for generating new IDs. If you wanted to override the default behavior and use a sequence, your @GeneratedValue annotation might look like this:

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

This would create a sequence named COMMENT_ID_SEQ to be used for generating new IDs for our Comment table. Hibernate offers many more options for a domain class's identifier, including UUID-based generation, or simply allowing your application to assign identifiers directly.

Note

When using the AUTO mode for ID generation, Hibernate will pick the ideal strategy based on the database you are using. However, for many databases, Hibernate will end up creating a single sequence to use across all your tables. This can get a bit messy, and we have often found that creating explicit sequences for each table is a little cleaner. If your domain model has some complexity to it, we recommend specifying a different sequence for each table or class.

Using Cascading Options to Establish Data Relationships

Associations within a domain model represent how different domain entities relate to one another. Often, these relationships can be expressed in layman's terms as parent-child relationships, meaning that one entity owns or encapsulates a collection of another entity. Within the database, associations are represented through table joins, but there is no clear analogue for representing the more hierarchical relationships we have within Java. This is where Hibernate comes in. Cascading options help to establish parent-child relationships, or more precisely, the rules for how operations such as save and delete that are applied to one entity should cascade to associated entities. This concept is often referred to as transitive persistence.

For example, within our gallery application, we would assert that ArtEntity owns a collection of Comment instances. This is logical since a Comment is attached to a particular ArtEntity instance. An end user can post a comment about a particular image, and this comment is typically relevant only to the image about which it was posted. Furthermore, if an ArtEntity instance is deleted, it doesn't make sense to keep its related Comment instances around anymore. In essence, comments are children of an ArtEntity.

Since comments can be considered children of an ArtEntity, we can assert that a save operation invoked on an ArtEntity should also cascade to any added or updated Comment instances associated to that ArtEntity instance. Additionally, should an ArtEntity be deleted, we would want the delete action to cascade to any associated Comment instances. We can represent these cascading rules using the following annotation:

@OneToMany(orphanRemoval = true, cascade = { javax.persistence.CascadeType.ALL })
public Set<Comment> getComments() {
    return comments;
}

In this case, we are setting orphanRemoval to true, which will also ensure that any dereferenced comments will also be deleted. We also specify a CascadeType of SAVE_UPDATE, which will ensure save and update operations invoked on an ArtEntity instance will be passed along to child Comment instances as well.

Adding Second-Level Caching

Hibernate allows entities, as well as association collections (a group of comments) to be implicitly cached. With caching enabled, Hibernate will first try to find an entity or collection in the cache before trying to query the database. Since loading data from the cache is far less expensive than performing a database operation, caching is another effective strategy for improving application performance.

Hibernate integrates with several caching frameworks, such as Ehcache, and provides a CacheManager interface if you want to add your own caching solution. Once integrated, caching happens implicitly, without requiring any additional coding, other than specifying caching rules for each entity and collection.

To get basic caching enabled on our domain model, we can add the following annotation to each domain entity, as well as its corresponding collections, to ensure they are appropriately cached:

@Entity
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ArtEntity implements Serializable {

    . . . Methods Omitted . . .

    @OneToMany(orphanRemoval = true, cascade = { javax.persistence.CascadeType.ALL })
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }

    . . . Methods Omitted . . .

}

Specifying a read-write caching strategy ensures that Hibernate will invalidate the cache whenever a particular domain instance is updated. This prevents stale data from being stored in the cache.

There are three types of caching options for Hibernate: domain, collection, and query. Domain and collection caching are demonstrated in the preceding example, as we have specified the @Cache annotation for the top-level domain entity as well as for the comments association.

Caching details should be adjusted using the configuration file appropriate for the caching implementation you have selected. In the case of Ehcache, you can configure specifics, such as the time-to-live and cache size on a domain-by-domain basis within the ehcache.xml file.

We will cover caching strategies in more detail in Chapter 9.

Using Polymorphism with Hibernate

For our gallery application, we require a few more classes to help provide the persistence details for all of the gallery's functionality. As a quick recap, here is an overview of our domain model, as it currently stands:

  • Person: Represents an administrative user or a registered user of our gallery application.

  • Exhibition: Organizes collections of images into logical groups.

  • ArtEntity: Represents an image in the application and contains metadata about the image, as well as its location.

  • Comment: Represents an individual comment that relates to a particular ArtEntity instance.

  • Our ArtEntity class represents basic metadata about an image, but what if we need to store an image in different resolutions, such as thumbnails, medium-resolution versions, and high-resolution versions? We could certainly insert additional fields into our ArtEntity class, but Hibernate provides a cleaner solution.

ORM solutions like Hibernate go far beyond mapping database fields to domain model instances. Object-oriented concepts, such as polymorphism, are also enabled by Hibernate and are an effective means for establishing a hierarchy of domain objects that share a set of core properties and functionality.

Rather than store an image path directly within our ArtEntity class, let's instead refactor this data into a separate base class called ArtData. We will then create three subclasses that each extend the ArtData class (and therefore share its properties) but are tailored to represent a particular type of image. We will define the following four new domain classes:

  • ArtData: The bulk of the properties will be stored here, since it is the base class.

  • ArtData_Gallery: This class will be used to represent the standard view of an image within the gallery listing pages.

  • ArtData_Thumbnail: This class will be used to represent thumbnails.

  • ArtData_Storage: This class will persist a high-resolution version of the image, suitable for archival purposes or for zoomed-in views.

Note

We won't include the entire source code for our domain model here. You can download the example code for this chapter if you would like to follow along.

Hibernate provides four different options for implementing polymorphism:

Implicit polymorphism: This option uses the Java inheritance structure without requiring these structural details to affect the database schema. In other words, using implicit polymorphism, you will be able to query a parent class, and Hibernate will issue select queries for all tables within the specified Java class hierarchy. While this strategy allows you to leverage the polymorphic structure inherent in your Java classes without affecting the database, these types of queries can be a bit inefficient, as Hibernate must do a lot more heavy lifting to translate distinct tables into a coherent class hierarchy, without being able to effectively leverage the database. The other polymorphic strategies rely on the database to some degree to delineate the associations between classes in the Java hierarchy.

Table-per-hierarchy: This option combines all the properties of a class hierarchy into a single table, using a discriminator field to help determine which Java type is represented by each row in the database. A discriminator is simply a table column, the value of which is used to specify to which class that particular row should be associated. The advantage of this approach is that all the necessary fields for any class within the hierarchy are included in a single table, without requiring the overhead of a database join. The disadvantage is that the design is not very normalized, and for any given type, there will likely be fields that will not be utilized. This can impose limitations on your database schema, such as preventing you from being able to specify not-null constraints. Since field requirements will differ between classes in the hierarchy and they are all shared within a single table, you must simplify the schema down to the lowest common denominator.

Table-per-subclass: Using this option, each Java class in the hierarchy is represented by a different table. Properties related to the parent class are persisted to a single table. The specific properties unique to each subclass are stored within their own database tables. A particular subclass in the hierarchy is then represented through a join between the parent table and the subclass table. The advantage of this approach is that the design is clean and normalized, since shared properties are stored in a single parent table and only subclass-specific attributes are sequestered into their own subclass tables. However, although cleaner from a relational database modeling perspective, you should consider the performance hit incurred by the necessity of joining tables together.

Table-per-concrete-class: This option requires that every Java class that is not declared as abstract be represented by its own table. Subclasses are not implemented as joins between multiple tables. Instead, all the properties of each class—including those properties inherited from a parent class—are persisted to their own table. This obviously requires a bit of redundancy, as the same fields across a class hierarchy will be present in each mapped table. However, Hibernate can implement polymorphic queries more efficiently by leveraging SQL unions across all tables mapped to a particular class hierarchy. The downside is the increased verbosity and redundancy in your database schema. Furthermore, Hibernate imposes limitations on the ID-generation strategy used by tables mapped with this polymorphic approach.

Which option to use really depends on your domain model. If there isn't too much disparity across classes within your class hierarchy, then the table-per-hierarchy option probably makes the most sense. In our case, this is the strategy we will employ.

Let's take a look at the base ArtData entity:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("GENERIC")
public class ArtData implements DomainObject {

    private Long id;
    private byte[] picture;
    private Integer version;

    public ArtData() {
    }

    public ArtData(byte[] picture) {
        this.picture = picture;
    }

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public byte[] getPicture() {
        return picture;
    }

    public void setPicture(byte[] picture) {
        this.picture = picture;
    }

    @Version
    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

}

Much of this class should look familiar. You will notice the standard JavaBean conventions, as well as the core Hibernate annotations. Let's focus on the annotations that enable the inheritance in our model.

The @Inheritance annotation tells Hibernate that we want to use inheritance and that we are defining our base class.

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

We are also specifying that we intend to use the table-per-hierarchy strategy (meaning that we want to persist all the fields in the entire hierarchy within a single table).

The @DiscriminatorColumn annotation provides Hibernate with the details about our discriminator. As mentioned earlier, the discriminator provides Hibernate with the clues it needs to infer to which Java type a particular database row corresponds. In our example, we are defining our discriminator column to be a String type. We could also use a char or an Integer.

Last, we define the discriminator value that each type will use through the @DiscriminatorValue annotation. In the case of the ArtData base class, we specify a value of GENERIC. So, for each ArtData instance that is persisted to the database, Hibernate will set the discriminator column to a value of GENERIC.

Next, we must define the classes that extend from our ArtData base class. Each class is fairly similar to one another in our scenario, but inheritance provides a clean way to classify the different types of images within our gallery application. Furthermore, this approach also provides future extension points, should we need to define additional metadata that only relates to a particular image type, such as a thumbnail aspect ratio or archival details for our ArtData_Storage class.

Here's our ArtData_Thumbnail class:

@Entity
@DiscriminatorValue("THUMBNAIL")
public class ArtData_Thumbnail extends ArtData {

    public ArtData_Thumbnail(byte[] picture) {
        this.setPicture(picture);
    }

    public ArtData_Thumbnail() {
    }

}

This is a fairly straightforward class. Notice, however, that we've set a discriminator value of THUMBNAIL.

Let's look at our ArtEntity class again, now with all of our refactorings applied:

@Entity
public class ArtEntity implements DomainObject {

    private Long id;
    private Integer version;
    private ArtData_Gallery galleryPicture;
    private ArtData_Storage storagePicture;
    private ArtData_Thumbnail thumbnailPicture;
    private Set<Category> categories = new HashSet();
    private Set<Comment> comments = new HashSet();

    public ArtEntity() {
    }
@Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }


    @Version
    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn()
    public ArtData_Gallery getGalleryPicture() {
        return galleryPicture;
    }

    public void setGalleryPicture(ArtData_Gallery pic) {
        this.galleryPicture = pic;
    }

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn()
    public ArtData_Storage getStoragePicture() {
        return storagePicture;
    }

    public void setStoragePicture(ArtData_Storage pic) {
        this.storagePicture = pic;
    }

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn()
    public ArtData_Thumbnail getThumbnailPicture() {
        return thumbnailPicture;
    }

    public void setThumbnailPicture(ArtData_Thumbnail pic) {
        this.thumbnailPicture = pic;
    }
@ManyToMany(mappedBy = "artEntities")
    public Set<Category> getCategories() {
        return categories;
    }

    @OneToMany(orphanRemoval = true, cascade = { javax.persistence.CascadeType.ALL })
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }

}

Notice that we have now defined a few one-to-one relationships for our thumbnailPicture, galleryPicture, and storagePicture properties. To simplify our code, we defined three separate one-to-one associations. However, we could have also chosen to put all the ArtData entities into a single collection, with a generic type of the ArtData base class. Since each image type is represented by a different subclass, it would be easy to differentiate between the different image types.

Also, notice that we have defined a many-to-many association to the Category class for the categories property. We have added the mappedBy hint here to indicate that the inverse side of this relationship is referenced by the artEntities property in the Comment class. For bidirectional many-to-many associations, we need to tell Hibernate which side of the collection is the owner. By adding the mappedBy attribute to the Comment class, we are asserting that the Category class owns the relationship.

Summary

In this chapter, we've introduced the fundamentals for defining a domain model with Hibernate. You learned about the mapping process and how you can use annotations to provide Hibernate with the appropriate clues to effectively map your object-oriented domain classes to your relational database.

We also examined association mapping, differentiating between the various cardinality options Hibernate provides. These details—such as whether to use many-to-many or one-to-many associations—have a significant impact on your domain model design, as well as the resultant database schema. Furthermore, it is important to think carefully about whether an association should be unidirectional or bidirectional. While bidirectional associations are often necessary to simplify reference walking and access, this option can have consequences in terms of circular dependencies that may complicate marshaling implementations.

Hibernate provides a powerful feature called cascading that allows you to associate the operations applied to one entity with its children entities so that these operations cascade. This feature is useful for ensuring that child entities are kept in sync with the state and life cycle of their parent entities.

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

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