Hibernate Collection Mapping
Mapping Bag in Collection Mapping
Map a Hibernate bag collection with an XML mapping file when the collection does not need a stored index or duplicate elimination.
What Is a Bag Collection?
A Hibernate bag is an unordered, non-indexed collection. It is similar to a list in that duplicate values are allowed, but Hibernate does not persist a position for each element. Therefore, a <bag> mapping does not use an <index> element.
<bag name="answers" table="question_answer"> <key column="question_id" not-null="true"/> <element column="answer" type="string"/> </bag>
Bag Compared with List and Set
| Collection | Index column | Duplicates | Ordering |
|---|---|---|---|
bag | No | Allowed | Not guaranteed |
list | Required | Allowed | Stored by position |
set | No | Removed according to equality | Not guaranteed unless ordered |
1. Create the Persistent Class
This forum example stores multiple text answers for each question. The Java property can be declared as a List for legacy XML compatibility, but the collection must be treated as unordered.
package com.example;
import java.util.ArrayList;
import java.util.List;
public class Question {
private Long id;
private String questionText;
private List<String> answers = new ArrayList<>();
protected Question() {
// Required by Hibernate.
}
public Question(String text, List<String> answers) {
this.questionText = text;
this.answers = answers;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getQuestionText() { return questionText; }
public void setQuestionText(String text) { this.questionText = text; }
public List<String> getAnswers() { return answers; }
public void setAnswers(List<String> answers) { this.answers = answers; }
}For new code, consider exposing a Collection<String> or using an explicit @ElementCollection mapping. The XML <bag> element controls the persistence semantics.
2. Create the Bag Mapping File
Create question.hbm.xml in the resources directory. The key links each answer row to its question and element stores the basic string value.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.example.Question" table="question">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="questionText" column="question_text"/>
<bag name="answers" table="question_answer">
<key column="question_id" not-null="true"/>
<element column="answer" type="string" not-null="true"/>
</bag>
</class>
</hibernate-mapping>No index column is created because the mapping does not promise a stable order. The collection table can contain duplicate answer values for the same question.
3. Create the Configuration File
Place hibernate.cfg.xml in src/main/resources. H2 is used here for a portable example.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
<property name="hibernate.connection.url">jdbc:h2:mem:forum-bag;DB_CLOSE_DELAY=-1</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="question.hbm.xml"/>
</session-factory>
</hibernate-configuration>Use migrations instead of create-drop for a production database.
4. Store Questions and Answers
Persist two questions with their answer collections in one transaction.
package com.example;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class StoreData {
public static void main(String[] args) {
try (SessionFactory factory = new Configuration()
.configure()
.buildSessionFactory();
Session session = factory.openSession()) {
Transaction transaction = session.beginTransaction();
try {
Question java = new Question(
"What is Java?",
List.of("Java is a programming language",
"Java is a platform"));
Question servlet = new Question(
"What is a Servlet?",
List.of("A Servlet is an interface",
"A Servlet is an API"));
session.persist(java);
session.persist(servlet);
transaction.commit();
System.out.println("Questions saved successfully");
} catch (RuntimeException exception) {
transaction.rollback();
throw exception;
}
}
}
}5. Fetch the Bag with HQL
Access the collection while the Hibernate session is open. Since a bag has no persisted index, do not rely on the order in which answers are returned.
try (Session session = factory.openSession()) {
List<Question> questions = session
.createQuery("from Question", Question.class)
.getResultList();
for (Question question : questions) {
System.out.println(
"Question: " + question.getQuestionText());
for (String answer : question.getAnswers()) {
System.out.println("- " + answer);
}
}
}For a large number of questions, plan fetching deliberately to avoid N+1 queries. Use a query with an explicit fetch join or an entity graph when the use case requires the answers immediately.
Bag of Entity References
A bag can also hold entity references. Replace <element> with a relationship element in legacy XML:
<bag name="answers" table="question_answer"> <key column="question_id" not-null="true"/> <one-to-many class="com.example.Answer"/> </bag>
For modern mappings, use @OneToMany. Define cascade, orphan removal, ownership, and equality behavior carefully before allowing duplicate entity references.
Bag Mapping Best Practices
- Use a bag only when order is not part of the business meaning.
- Use a list with an order column when the position must be preserved.
- Use a set when duplicate values are not valid.
- Keep the collection initialized and avoid
null. - Do not access a lazy bag after its session has closed.
- Use
not-null="true"when every collection row must have an owner. - Use database indexes on foreign-key columns for large collections.
- Use schema migrations rather than automatic schema updates in production.
