Hibernate Tutorial

Core Hibernate

Hibernate Mapping

Hibernate Annotations

Hibernate with Spring Framework

Hibernate with Database

Hibernate Log4j

Inheritance Mapping

Hibernate - Bag Mapping

In Hibernate, the term "bag" usually refers to a collection that can contain duplicate elements and does not guarantee any specific order (similar to a java.util.List). In the context of ORM (Object-Relational Mapping), this means mapping a one-to-many or many-to-many association to a collection of this nature.

In this tutorial, we'll discuss how to use bag mapping in Hibernate.

Setup:

  1. Create a new Maven project.
  2. Add Hibernate and your preferred RDBMS JDBC driver as dependencies in your pom.xml.
  3. Set up your hibernate.cfg.xml configuration file.

Example: Bag Mapping in a One-to-Many Relationship

Let's say we have two entities: Author and Book. An author can write multiple books, but each book has only one author. Here, the collection of books for an author is a bag, as an author might write multiple editions of the same book title.

1. Define the Entities:

Author.java:
@Entity
@Table(name="author")
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name="author_id")
    private List<Book> books = new ArrayList<>();

    // Constructors, getters, setters, etc.
}
Book.java:
@Entity
@Table(name="book")
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String title;

    // No need to define the Author relationship here for this example.

    // Constructors, getters, setters, etc.
}

2. Persisting the Entities:

To save an author with a couple of books:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

Author author = new Author();
author.setName("George Orwell");

Book book1 = new Book();
book1.setTitle("1984");

Book book2 = new Book();
book2.setTitle("1984"); // Intentional duplicate for demonstration.

author.getBooks().add(book1);
author.getBooks().add(book2);

session.persist(author);
tx.commit();
session.close();

Here, you can see that we intentionally added two books with the same title "1984" to demonstrate that bags can hold duplicate elements.

Drawbacks of Bags:

  1. Performance Issues: Hibernate might need to recreate the entire bag (delete all and re-insert) even if you make a minor change to the collection. This is because Hibernate might not be able to figure out which specific elements have been added or removed from the bag.

  2. No Order: Bags don't maintain any order, so if your application has a requirement to fetch elements in a specific order, then bags might not be the best choice.

  3. Semantic Ambiguity: It's not always clear when you would use a bag over a set or list, as a bag is less semantically clear. It's a collection that allows duplicates like a list but doesn't guarantee order.

In many situations, a Set (which does not allow duplicates) or a List (which maintains order) may be a more appropriate choice than a bag. However, understanding the concept and usage of bag mapping can be beneficial in specific scenarios where neither order nor uniqueness is crucial.

  1. Hibernate bag mapping example:

    In Hibernate, a bag represents an unordered collection of elements, typically used for mapping Java collections like List or Set to database tables.

    @Entity
    @Table(name = "orders")
    public class Order {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "order_id")
        private Long id;
    
        @ElementCollection
        @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
        @Column(name = "item")
        private List<String> items;
    
        // Other properties and methods
    }
    
  2. Bag mapping in Hibernate XML:

    The equivalent XML mapping for bag mapping in Hibernate would look like this:

    <class name="Order" table="orders">
        <id name="id" type="long">
            <generator class="identity"/>
        </id>
        <bag name="items" table="order_items">
            <key column="order_id"/>
            <element column="item" type="string"/>
        </bag>
    </class>
    
  3. Mapping bag collections in Hibernate:

    Bag collections are mapped using @ElementCollection and @CollectionTable annotations.

    @Entity
    @Table(name = "orders")
    public class Order {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "order_id")
        private Long id;
    
        @ElementCollection
        @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
        @Column(name = "item")
        private List<String> items;
    
        // Other properties and methods
    }
    
  4. Hibernate bag mapping cascade options:

    Cascade options determine how changes to the bag collection should propagate to associated entities.

    @Entity
    @Table(name = "orders")
    public class Order {
        // ...
    
        @ElementCollection
        @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
        @Column(name = "item")
        @Cascade(value = org.hibernate.annotations.CascadeType.ALL)
        private List<String> items;
    
        // ...
    }
    
  5. Inverse attribute in Hibernate bag mapping:

    The inverse attribute in Hibernate indicates whether the bag is responsible for managing the association.

    @Entity
    @Table(name = "orders")
    public class Order {
        // ...
    
        @ElementCollection
        @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
        @Column(name = "item")
        @InverseJoinColumns(@JoinColumn(name = "item_id"))
        private List<String> items;
    
        // ...
    }
    
  6. Lazy loading with Hibernate bag mapping:

    Lazy loading ensures that the bag collection is loaded from the database only when accessed.

    @Entity
    @Table(name = "orders")
    public class Order {
        // ...
    
        @ElementCollection
        @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
        @Column(name = "item")
        @LazyCollection(LazyCollectionOption.TRUE)
        private List<String> items;
    
        // ...
    }
    
  7. Bidirectional association with bag mapping in Hibernate:

    Bidirectional associations involve two entities having references to each other.

    @Entity
    @Table(name = "orders")
    public class Order {
        // ...
    
        @OneToMany(mappedBy = "order")
        @LazyCollection(LazyCollectionOption.TRUE)
        private List<OrderItem> orderItems;
    
        // ...
    }
    
    @Entity
    @Table(name = "order_items")
    public class OrderItem {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "item_id")
        private Long id;
    
        @ManyToOne
        @JoinColumn(name = "order_id")
        private Order order;
    
        // Other properties and methods
    }
    
  8. Indexed bag mapping in Hibernate:

    Indexed bags allow accessing elements by index, similar to a List with an ordered index.

    @Entity
    @Table(name = "orders")
    public class Order {
        // ...
    
        @ElementCollection
        @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
        @Column(name = "item")
        @OrderColumn(name = "item_index")
        private List<String> items;
    
        // ...
    }