I've got 2 tables. One for questions and one for possible answers. For example, I'm modelling:
"Do you own a dog?" Yes[ ], No[ ].
So I have a set of questions and a set of possible answers. What I want to know how do I represent this in JPA (note this is not about capturing the answer, but displaying the question and populating a selection box).
So far I have:
#Entity(name="QUESTIONS")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int order;
private String questionTitle;
private String questionText;
private Set<AnswerOption> possibleAnswers;
....
}
It is the private Set<AnswerOption> possibleAnswers; part I'm having trouble with. How do I get this to be pre-populated with the possible range of answers?
The way it is modelled above will provide a Set variable to store answers in.
Am I thinking about this the wrong way? Should I use code to populate the database and assign the same AnswerOption object(s) to different Question objects?
Thanks for any help.
Adam
As Hibernate is essentially an ORM tool, it just takes care of mapping your Question and AnswerOption classes and instances to the defined tables.
What you need is to initialize data, not data structure. So, you've got to populate all of your Question instances with their possible AnswerOption instances in some sort of initQuestions() initialization method.
Also, you'd better note whether these Questions are already initialized.
You should look at this first from a database relational modeling point of view, before trying to map that relationship in JPA. First, you need to define how questions and answers are linked at the database level. I presume you use a foreign key or association table. Something like this:
CREATE TABLE questions (
id INT NOT NULL PRIMARY KEY,
text VARCHAR(255)
);
CREATE TABLE answers (
id INT NOT NULL PRIMARY KEY,
text VARCHAR(255)
);
CREATE TABLE question_answers (
id INT NOT NULL PRIMARY KEY,
question_id INT NOT NULL,
answer_id INT NOT NULL,
KEY k_question_id (question_id),
KEY k_answer_id (answer_id),
CONSTRAINT fk_question_answers_question_id FOREIGN KEY (question_id)
REFERENCES questions(id),
CONSTRAINT fk_question_answers_answer_id FOREIGN KEY (answer_id)
REFERENCES answers(id)
);
This defines the relationship between questions and answers as an association table, which you can then map in JPA thusly:
#Entity(name="QUESTIONS")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int order;
private String questionTitle;
private String questionText;
#JoinTable(name = "question_answers", joinColumns = { #JoinColumn(name = "question_id", unique = true) }, inverseJoinColumns = { #JoinColumn(name = "answer_id") })
private Set<AnswerOption> possibleAnswers;
}
The annotations for using a foreign key (as opposed to an association table) will be different of course, if you want to go that route leave a comment and I'll whip up an example. This should be enough to get you started.
Related
I have two different tables, both of which have composite embedded keys. Both composite keys have in composition the same id A_ID.
I want to join table M with table D in a one to many relationship using a join-table.
The following are some pseudo-java code converted from XML ORM mappings. So please excuse any mistakes written here. The mappings in the final code work so the typos here are not to blame.
#Entity()
public class M {
#EmbeddedId()
private EmbeddedMId id;
#OneToMany(name="d", #JoinTable(name="M-D",
joinColumns={
#JoinColumn(name="M_ID", referencedColumnName="M_ID"),
#JoinColumn(name="A_ID", referencedColumnName="A_ID", table="M")
},
inverseJoinColumns={
#InverseJoinColumn(name="D_ID", referencedColumnName="D_ID"),
#InverseJoinColumn(name="A_ID", referencedColumnName="A_ID", table="D", insertable="false", updatable="false")
}
))
private Set<D> dSet;
}
#Embeddable()
public class EmbeddedMId {
#Basic() private String A_ID;
#Basic() private String M_ID;
}
#Embeddable()
public class EmbeddedDId {
#Basic() private String A_ID;
#Basic() private String D_ID;
}
As you can see, the embeddables both use A_ID therefore we tried to make the 2nd A_ID in the join-table be readonly. The application starts and the mappings seem to be okay.
The problem is whenever I want to insert a new D object in the M entity, hibernate throws an SQL error invalid column index because while the prepared statement is correct as seen bellow, hibernate only provides the first 2 parameters instead of all three. (Values provided by hibernate are (VALID_M_ID, VALID_A_ID) instead of providing 3 values)
INSERT INTO M_D("M_ID", "A_ID", "D_ID") VALUES (?, ?, ?)
If I rename the 2nd inverseJoinColumn to have a new column name and make it insertable/updatable, the problem is solved. But this means that the A_ID is duplicated in both A_ID and A_REPEAT_ID column and this is what I want to avoid.
#InverseJoinColumn(name="A_REPEAT_ID", referencedColumnName="A_ID", table="D")
Is there a way to tell Hibernate that my EmbeddedDId needs to be mapped over the D_ID and A_ID (readonly) correctly when doing the insertions?
I hope my explanation is clear enough, but feel free to ask for any clarifications.
Hibernate version is 5.2.17-FINAL
EDIT
The only other entity that is important in this case is pretty simple. But as requested I'll write it here
#Entity()
public class D {
#EmbeddedId()
private EmbeddedDId id;
/* other basic fields here */
}
I don't think insertable = false, updatable = false does what you want here. If you want the target column A_ID on D to be readonly, then you will have to map the column in the target entity D and specify there that the column is insertable = false, updatable = false but not on this association.
I have two tables, one named "Category" and the other "Rule" that are related logically with One to One relationship using a field (code) different than the Primary Key (PK) of table and not phisically managed with a Foreign Key (FK):
CATEGORY
ID (PK) NUMBER
COD_RULE VARCHAR
NAME VARCHAR
.....
RULE
ID (PK) NUMBER
CODE VARCHAR
TYPE VARCHAR
.....
I haven't on Rule table FK to category ID but only unique constraint (the relation is 1 to 1)
Implemented in this way in JPA
public Category implement Serializable {
#Id
#Column (name="ID")
private Long id;
#NotNull
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="CODE_RULE" referencedColumnName="CODE", nullable=false)
private Rule;
#Column (name="NAME")
private String name;
//Getter and Setter methods
.......
}
public Rule implement Serializable {
#Id
#Column (name="ID")
private Long id;
#NotNull
#Column (name="CODE")
private String code;
#Column (name="TYPE")
private String type;
//Getter and Setter methods
.......
}
I need to:
When retrieve Categories obtain also all informations of associated Rule
SELECT c.*, r.type FROM Category c LEFT OUTER JOIN Rule r WHERE c.CODE_RULE = r.CODE
When edit Category maintain aligned CODE_RULE with CODE, so If I change CODE_RULE I would yo change automatically the CODE on Rule
UPDATE Category SET COD_RULE='5A', NAME='Test' WHERE ID=1
UPDATE Rule SET CODE='5A' WHERE CODE='AB'
I see on the specification that:
There are three cases for one-to-one associations: either the
associated entities share the same primary keys values, a foreign key
is held by one of the entities (note that this FK column in the
database should be constrained unique to simulate one-to-one
multiplicity), or a association table is used to store the link
between the 2 entities (a unique constraint has to be defined on each
fk to ensure the one to one multiplicity).
But with this implementation satisfy point 1. But not the point 2.
Suppose that I've already created Category (on ID = 1) and associated rule, when I edit category (having CODE_RULE = CODE = "AB") and change the code to "5A":
#PersistentContext
private EntityManager em;
.......
Category cat = em.find(Category.class, 1L);
cat.setName("Test");
cat.getRule().setCode("5A");
em.merge(cat);
I see that the code has been updated on Rule but not in Category:
BEFORE EDIT
Category (ID, COD_RULE, NAME) --> (1, AB, First Category)
Rule (ID, CODE, TYPE) --> (10, AB, C)
AFTER EDIT
Category (ID, COD_RULE, NAME)--> (1, AB, Test)
Rule (ID, CODE, TYPE) --> (10, 5A, C)
How can I do this work in JPA?
Is this type of operation supported in the JPA specification?
Is there an alternative (i.e. I have to merge before Rule and then Category)?
From your datamodel, it looks more like a Many To One relationship between Category and Rule, given in your data model only restrict each Category can refer to [0..1] Rule, but not restricting how many Categories that a Rule can be referred by.
Based on your comment, it seems that you can change the data model. Normally if it is a ~ToOne relationship, you should have the referring side referring as FK, which looks like this:
(Tables)
CATEGORY (
CATEGORY_ID NUMBER PK,
CATEGORY_CODE VARCHAR, // Unqiue
RULE_ID NUMBER FK to RULE,
... (Don't refer by RULE_CODE!!)
)
RULE (
RULE_ID NUMBER PK,
RULE_CODE VARCHAR, // unique, can be updated
...
)
Entity should look like
class Category {
#Id #Column(name="CATEGORY_ID)
Long id;
#ManyToOne // or #OneToOne if you really insist
#JoinColumn(name="RULE_ID)
Rule rule;
)
(class Rule is straight-forward, I will skip)
The HQL you mentioned should be
// When retrieving Category together with Rule
from Category c join fetch c.rule
for Point 2, as you mentioned in comment, you are trying to align Rule's code with Category's code, when Category's code is updated. This should be implemented as:
class Category {
//.....
public void setCode(String code) {
this.code = code;
this.rule.setCode(code);
}
//....
)
Base on personal experience, when using JPA, life will be much easier to drive data model base on domain model design. It should save a lot of problem caused by "data-model that looks tolerable".
I want to create a java class that contains only 1 column from OneToMany ManyToOne etc. type connection not the whole row.
How can I do that?
(I'm not sure that I could express myself so I made an example)
TABLE e_skill
(
id int NOT NULL AUTO_INCREMENT,
skill_name VARCHAR (20) NOT NULL,
PRIMARY KEY (id)
);
TABLE t_person
(
id int NOT NULL AUTO_INCREMENT,
user_id int NOT NULL,
primary_skill int,
PRIMARY KEY (id),
FOREIGN KEY (primary_skill) REFERENCES e_skill(id)
);
TABLE t_secondaryskills
(
id int NOT NULL AUTO_INCREMENT,
t_person_id int NOT NULL,
skill_name int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (t_person_id) REFERENCES t_person(id),
FOREIGN KEY (skill_name) REFERENCES e_skill(id)
);
public enum Skill {
...
}
#Entity
#Table(name = "t_person")
public class Employee {
#Id
#GeneratedValue
private Integer id;
#ManyToOne
#Enumerated(EnumType.STRING)
//????????
//get skill_name column from e_skill
//????????
private Skill primarySkill;
#OneToMany
#Enumerated(EnumType.STRING)
//????????
//get skill_name column from e_skill
//????????
private Set<Skill> secondarySkills;
//getters setters
}
The only way I could do it now is to create a Entity to represent the e_skill table, I want to avoid that, because I only need 1 column from it.
If I understand your question correctly, you can't do what you want because of the secondary skills (because it's a collection). You can only map the primary skill name though using the #SecondaryTable annotation.
When you map things using an ORM there's no such thing as I only want a column in this scenario as you're mapping Objects, and usually in your objects you don't want to replicate data (unless they are outside your domain model). If this is unacceptable for you, I suggest you to take a look at other tools like myBtais, which gives you full control on the data you get back.
So bottom line, map your skill as an entity and live with it even if it has many columns, or choose a different tool (but not an ORM).
I'm currently using Spring and Hibernate framework, and have an entity with:
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID")
private Long id;
#Column(name="ACC_ID")
private Long accId;
Now, in a specific case I'd like to merge an object in the database using column "ACC_ID" instead of "ID", however, I do not want to assign #Id to accId because I do not want to change the entity itself.
Is there anything I can do on the merge function? (But apparently merge takes no other parameter than an object)
entityManager.merge(entityObject)
Thanks in advance for any clue or help. =)
entityManager.merge(entityObject) can be used if it is your primary key based.
If it is another unique constraint you'd have to handle it by yourself. First try to find an entity with that value (with a query).
If a match is found, copy the primary key to your new entity before saving as normal.
For example:
public Entity save(Entity entity, boolean rollback) {
// look for a match, you'll have to implement your own method here
Entity match = getEntityByValue("column_name", entity.getMergeColumn());
if (match != null) {
// copy the primary key
entity.setId(match.getId());
}
// save the entity
save(entity, rollback);
}
I'm having trouble using hibernate to persist a simple java object containing just a list of phrases in the form of strings.
To store this in a MySql database I have two tables:
phrase_list - columns:
group_id, phrase_id, list_position (which together make up the primary key; also there is a foreign key constraint on phrase_id from the phrase table)
phrase - columns: phrase_id, phrase (phrase is a varchar with a unique index)
I would like to simply be able to annotate the object containing the list of phrases in order to persist it, but nothing I've tried has worked, I've tried examples and variations of using #ElementCollection and #OneToMany with another Entity representing the phrase, but no matter what I try I can't seem to get this to work.
For example a phrase list: {"a","b","b"} would be stored as:
**(phrase table)** **(phrase_list table)**
phrase_id phrase group_id phrase_id list_position
1 'a' 1 1 0
2 'b' 1 2 1
1 2 2
Here is the attempt to what I thought would work; I added an extra id field because I could see no way to use the foreign key as part of the id, and annotating the list with #Id didn't work.
#Entity #Table(name = "phrase_list")
public class PhraseList {
#ElementCollection
#CollectionTable(
name="phrase",
joinColumns=#JoinColumn(name="phrase_id")
)
#OrderColumn(name="list_position")
#Column(name="phrase")
private List<String> phrases;
#Id #GeneratedValue
#Column(name = "list_id")
private Long id;
#Column(name = "goup_id")
private Long groupId;
public void setResults(List<String> results){
this.results = results;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
}
I get this exception:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Cannot add or update a child row: a foreign key constraint fails (test_db.phrase_list, CONSTRAINTfk_phrase_list_phrase_idFOREIGN KEY (phrase_id) REFERENCESphrase(phrase_id`) ON DELETE NO ACTION ON UPDATE )
Is there any way to get this to work simply by annotating the plain Java object? If not what is the best way to achieve this?
If I can't get it to work with annotation I would probably write custom queries and/or specialized methods for persisting the object. I.e., a method that is coupled to that particular class in knowing how to persist its objects, which I hoped to avoid.