Hibernate:如何持久化以Question对象为键的HashMap?
Hey there! Mapping a HashMap with an entity as the key and polymorphic values (either Integer or an Answer entity) in Hibernate is totally doable, but it requires handling two specific challenges: using an entity as the map key and supporting multiple value types. Let's walk through exactly how to set this up.
Step 1: Prepare Your Entity Classes
First, make sure your Question and Answer entities are properly annotated (if you're using Answer as a value type):
Question Entity
@Entity public class Question { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String questionText; // Add other fields like options, difficulty, etc. // Critical: Implement equals() and hashCode() based on the ID @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Question)) return false; Question question = (Question) o; return Objects.equals(id, question.id); } @Override public int hashCode() { return Objects.hash(id); } // Getters and Setters }
Answer Entity (if using as a value type)
@Entity public class Answer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String answerText; // Add other relevant fields // Getters and Setters }
Step 2: Map the Polymorphic HashMap in the User Entity
The core work happens in your User class. We'll use Hibernate's @Any annotation to handle the polymorphic values, and @MapKeyJoinColumn to map the Question entity as the map key.
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; // The polymorphic HashMap mapping @ElementCollection @CollectionTable( name = "user_question_answers", joinColumns = @JoinColumn(name = "user_id") ) @MapKeyJoinColumn(name = "question_id") // Maps Question entity to the 'question_id' column @Any(metaColumn = @Column(name = "answer_type")) @AnyMetaDef( idType = "long", metaType = "string", metaValues = { @MetaValue(targetEntity = Integer.class, value = "INT"), @MetaValue(targetEntity = Answer.class, value = "ANSWER") } ) @Column(name = "answer_int_value") // Stores Integer answers @JoinColumn(name = "answer_id") // Stores foreign key to Answer entity private Map<Question, Object> questionAnswers = new HashMap<>(); // Getters and Setters }
Let's Break Down the Annotations
@ElementCollection: Tells Hibernate this is a collection of non-entity values (or polymorphic values) associated with the User. It will create a separate join table (user_question_answers) to store these mappings.@CollectionTable: Defines the name of the join table and the foreign key linking back to the User table.@MapKeyJoinColumn: Specifies that the map's key is theQuestionentity, and it will store theQuestion's ID in thequestion_idcolumn of the join table.@Any: Enables polymorphic mapping for the map's value. ThemetaColumnstores a string identifier to distinguish between Integer and Answer values.@AnyMetaDef: Maps the string identifiers frommetaColumnto actual types. Here, "INT" maps toInteger.class, and "ANSWER" maps toAnswer.class. TheidTypespecifies the data type used to store the value (both Integer and Answer's ID uselong).@Columnand@JoinColumn: These define the columns that store the actual values. For Integer answers, the value goes intoanswer_int_value; for Answer entities, we store the foreign key inanswer_id. One of these columns will be null for each row, depending on the value type.
Important Notes
- equals() and hashCode() for Question: Since
Questionis used as a HashMap key, you must implementequals()andhashCode()based on its persistent ID (not transient state). This ensures Hibernate can correctly manage the map keys. - Type Casting: When retrieving values from the map, you'll need to cast them to the appropriate type:
// Get an Integer answer Integer intAnswer = (Integer) user.getQuestionAnswers().get(someQuestion); // Get an Answer entity Answer entityAnswer = (Answer) user.getQuestionAnswers().get(anotherQuestion); - Database Table Setup: The
user_question_answerstable will need nullable columns foranswer_int_valueandanswer_id, since only one will be populated per row. - Alternative: Split the Map: If your use case allows, splitting into two separate maps (one for Integer answers, one for Answer entities) simplifies mapping (using standard
@ElementCollectionwith@MapKeyJoinColumnfor each). But if you need a single map, the above approach works.
内容的提问来源于stack exchange,提问作者William Toscano
相关产品推荐
相关产品推荐

