I have two entities: Questionnaire and QuestionnaireTime. Questionnaire's id is a foreign key in QuestionnaireTime. So the relationship in my QuestionnaireTime entity looks like this:
#JoinColumn(name = "questionnaireid", referencedColumnName = "id")
#ManyToOne(cascade = CascadeType.PERSIST)
private Questionnaire questionnaireid;
So what I'm trying to do is to add multiple QuestionnaireTime records for one Questionnaire. If I remove the CascadeType.PERSIST part in my relationship, my persist is not done. And when I use cascade, I get several new records in my main table Questionnaire and that's not what I want.
For example when I want to add three QuestionnaireTime's for a certain Questionnaire, the three records are inserted in my QuestionnaireTime table but also 3+1 records are added in Questionnaire.
If you need more explanation. This is my managed bean, the part that I'm trying to add multiple QuestionnaireTime records in one Questionnaire:
NB - current is my Questionnaire object
else if (current.getType().equals("frequent")) {
int iteration = 1;
currentQuestionnaireTime = new QuestionnaireTime();
if (!selectDateList.isEmpty()) {
for (String insertedDate : selectDateList) {
currentQuestionnaireTime.setId(0);
currentQuestionnaireTime.setQuestionnaireid(current);
getEjbQuestionnaireTimeFacade().create(currentQuestionnaireTime);
iteration++;
}
}
}
try {
getFacade().create(current); // my Questionnaire facade
} catch (EJBException ejbe) {
ejbe.getCause();
}
A few things,
questionnaireid - this is a very bad field name, questionnaire would make sense.
currentQuestionnaireTime.setId(0); - you should not be changing the id of an existing object, instead create a new object
getEjbQuestionnaireTimeFacade().create() - what does this do? If you need the reference to the current, then the current should be persisted first. If you EJB remote? If it is, then either make it local, or ensure you use merge() not persist(), as you new object has a reference to a detached object. Or find the reference in the current persistence context.
Related
I have a many to many relationship between a User and a Trip entity with two foreign keys. I am trying to add a Trip to the User and even though there is a such an ID in my Users table, I receive the following exception:
Caused by: org.postgresql.util.PSQLException: ERROR: insert or update on table "UserTrip" violates foreign key constraint "user_id"
Detail: Key (user_id)=(1) is not present in table "User".
User side of the many to many relationship:
#ManyToMany
#JoinTable(name = "\"UserTrip\"", schema = "\"TransportSystem\"", joinColumns = {
#JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "trip_id") })
private List<Trip> trips = new ArrayList<>();
Trip side of the many to many relationship:
#ManyToMany(mappedBy = "trips")
private List<User> users = new ArrayList<>();
DAO function to add a trip:
public void addTrip(int id, Trip trip) {
executeInsideTransaction(entityManager -> {
User user = entityManager.find(User.class, id);
user.getTrips().add(trip);
});
}
My little helper function to handle transactions within the same dao:
private void executeInsideTransaction(Consumer<EntityManager> action) {
EntityTransaction tx = entityManager.getTransaction();
try {
tx.begin();
action.accept(entityManager);
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
}
}
This is where I call to add the trip (don't think any more context is needed, if you wish, I can provide more.)
UserService userService = new UserService();
User user = userService.getById(1);
userService.addTrip(1, newTrip);
Things to note:
The entity is "User" but the table it is mapped to is called "Users" since in PostgreSQL the User is a reserved keyword.
I tried MERGE and REMOVE cascades and a lazy fetch type on the User side
I tried to pass the whole User object to the addTrip function and then use entityManager.merge() but then as read here on stackoverflow I decided to use entityManager.find() to load the user by id from the database directly and then add a role and commit the transaction. Unfortunately, both cases yield the same result (this exception).
Needless to say, there is a user_id = 1 in the database.
I would appreciate your input. I know there are many threads regarding this particular exception but honestly I seem unable to resolve it.
This has been resolved, to anyone wondering:
The problem comes from the fact that User is a reserved keyword in PostgreSQL. I tried multiple times to avoid problems with it, I created a Users table and mapped my User entity to Users table. So far there were no problems with it.
But once a joined table is involved, things get complicated. In my case, I had a UserTrip joined table with a many to many relationship and Hibernate is looking for a user_id from User. I found no way to explicitly tell hibernate to take Users rather than User, that's why I decided to name my table UsersTrip and everything has been resolved.
Lesson learned - avoid using words similiar to keywords where possible.
I use Envers to audit my data and sometimes the value of the _MOD is incorrect. It stays at 0 instead of 1 when I am adding an element in my list. But it happens only in a specific case.
My entity:
#Entity
#Table(name = "PERSONNE")
#Audited(withModifiedFlag = true)
public class PersonEntity {
#Id
#Column(name = "ID_PERSONNE")
private Long id;
#Column(name = "NAME", length = 100)
private String name;
#Audited( withModifiedFlag = true, modifiedColumnName = "SERVICES_MOD")
private Set<PersonneServiceEntity> services = new HashSet<>(); // Entity with attributs, gettters, setters and envers annotations...
#Audited( withModifiedFlag = true, modifiedColumnName = "OPT_INS_MOD")
private Set<OptinEntity> optIns = new HashSet<>();// Entity with attributs, gettters, setters and envers annotations...
// more fields
// + getters, setteurs, equals, tostring
my service:
// personFromDB is retrieve via an Id
private void update(PersonEntity personFromRequest, PersonEntity personFromDB) {
personFromDB.setName(personFromRequest.getName());
updateServices(personFromRequest, personFromDB); // add new support to the list
updateOptins(personFromRequest, personFromDB); // add new services to the list
personDao.saveAndFlush(personFromDB);
}
This is were the magic happens: When I am updating name, services and optIns. Values in my database are all correct, my entity is correctly persisted, except one envers's column: OPT_INS_MOD ( OPT_INS_MOD == 0).
But if I am not updating the name ( line commented ) then everything is correctly persisted including all _MOD values ( OPT_INS_MOD == 1 and SERVICES_MOD ).
And finally if I am switching updateSupport(personFromRequest, personFromDB) and updateServices(personFromRequest, personFromDB), in this case OPT_INS_MOD is correct but not SERVICES_MOD.
My guess is that there is a problem when Envers is getting all modified fields. Because it does not make any sense to me.
Any ideas? I am using Envers version 4.3.11.Final
I'm not sure this will help you because it doesn't sound like the same problem but I've noticed a weirdness with modified flags and collections.
I get my entities back from the front end converted from JSON back to POJOs. In order to keep from having a transient object error from Hibernate, I need to reset the value in the #Id field (which was never sent to the FE). This works fine for 1-1 entities.
On collections, I found that if I create a new instance of the collection class and fill it with refreshed entities from the old collection and then assign that new collection to the old attribute, the modified flag is set to true.
However, if I fill a new collection with refreshed entities, clear() the old collection, then add all the items in the new collection, modified flag will be false unless there were actual changes to the collection.
I have some JPA code that is causing me some consternation. There are two objects; a Patient and a PatientUR (identifier) related as follows:
#OneToMany(mappedBy="patient", cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.EAGER)
private List<PatientUR> urs;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name="patient_id")
private Patient patient;
I can create a patient and patientUR(s) within a transaction and they are persisted correctly. However, there are occasions (some time later) when I need to retrieve the patientUR(s) from a persisted patient. I use this approach:
// find patient using an initial reference ur
PatientUR myPatientUR = patientURService.find(ur0); // is there something broken here?
Patient myPatient = myPatientUR.getPatient();
// Modify patient if required
// retrieve all UR's
List<PatientUR> myRegisteredURList = myPatient.getUrs();
The issue is that myRegisteredURList always returns an empty list. The patient lookup and ur retrieval happens within a transaction.
Not sure what I am missing here?
Just modified the code with:
List<PatientUR> myRegisteredURList = patientURService.findByPatient(myPatient);
this return correctly - that is searching for PatientUR's by a PatientID rather than by retrieving the PatientUR list from the Patient object.
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 trying to follow the JPA tutorial and using ElementCollection to record employee phone numbers:
PHONE (table)
OWNER_ID TYPE NUMBER
1 home 792-0001
1 work 494-1234
2 work 892-0005
Short version
What I need is a class like this:
#Entity
#Table(name="Phones")
public class PhoneId {
#Id
#Column(name="owner_id")
long owner_id;
#Embedded
List<Phone> phones;
}
that stores each person's phone numbers in a collection.
Long version
I follow the tutorial code:
#Entity
#Table(name="Phones")
public class PhoneId {
#Id
#Column(name="owner_id")
long owner_id;
#ElementCollection
#CollectionTable(
name="Phones",
joinColumns=#JoinColumn(name="owner_id")
)
List<Phone> phones = new ArrayList<Phone>();
}
#Embeddable
class Phone {
#Column(name="type")
String type = "";
#Column(name="number")
String number = "";
public Phone () {}
public Phone (String type, String number)
{ this.type = type; this.number = number; }
}
with a slight difference that I only keep one table. I tried to use the following code to add records to this table:
public static void main (String[] args) {
EntityManagerFactory entityFactory =
Persistence.createEntityManagerFactory("Tutorial");
EntityManager entityManager = entityFactory.createEntityManager();
// Create new entity
entityManager.getTransaction().begin();
Phone ph = new Phone("home", "001-010-0100");
PhoneId phid = new PhoneId();
phid.phones.add(ph);
entityManager.persist(phid);
entityManager.getTransaction().commit();
entityManager.close();
}
but it keeps throwing exceptions
Internal Exception: org.postgresql.util.PSQLException: ERROR: null
value in column "type" violates not-null constraint Detail: Failing
row contains (0, null, null). Error Code: 0 Call: INSERT INTO Phones
(owner_id) VALUES (?) bind => [1 parameter bound] Query:
InsertObjectQuery(tutorial.Phone1#162e295)
What did I do wrong?
Sadly, i think the slight difference that you only keep one table is the problem here.
Look at the declaration of the PhoneId class (which i would suggest is better called PhoneOwner or something like that):
#Entity
#Table(name="Phones")
public class PhoneId {
When you declare that a class is an entity mapped to a certain table, you are making a set of assertions, of which two are particularly important here. Firstly, that there is one row in the table for each instance of the entity, and vice versa. Secondly, that there is one column in the table for each scalar field of the entity, and vice versa. Both of these are at the heart of the idea of object-relational mapping.
However, in your schema, neither of these assertions hold. In the data you gave:
OWNER_ID TYPE NUMBER
1 home 792-0001
1 work 494-1234
2 work 892-0005
There are two rows corresponding to the entity with owner_id 1, violating the first assertion. There are columns TYPE and NUMBER which are not mapped to fields in the entity, violating the second assertion.
(To be clear, there is nothing wrong with your declaration of the Phone class or the phones field - just the PhoneId entity)
As a result, when your JPA provider tries to insert an instance of PhoneId into the database, it runs into trouble. Because there are no mappings for the TYPE and NUMBER columns in PhoneId, when it generates the SQL for the insert, it does not include values for them. This is why you get the error you see - the provider writes INSERT INTO Phones (owner_id) VALUES (?), which PostgreSQL treats as INSERT INTO Phones (owner_id, type, number) VALUES (?, null, null), which is rejected.
Even if you did manage to insert a row into this table, you would then run into trouble on retrieving an object from it. Say you asked for the instance of PhoneId with owner_id 1. The provider would write SQL amounting to select * from Phones where owner_id = 1, and it would expect that to find exactly one row, which it can map to an object. But it will find two rows!
The solution, i'm afraid, is to use two tables, one for PhoneId, and one for Phone. The table for PhoneId will be trivially simple, but it is necessary for the correct operation of the JPA machinery.
Assuming you rename PhoneId to PhoneOwner, the tables need to look like:
create table PhoneOwner (
owner_id integer primary key
)
create table Phone (
owner_id integer not null references PhoneOwner,
type varchar(255) not null,
number varchar(255) not null,
primary key (owner_id, number)
)
(I've made (owner_id, number) the primary key for Phone, on the assumption that one owner might have more than one number of a given type, but will never have one number recorded under two types. You might prefer (owner_id, type) if that better reflects your domain.)
The entities are then:
#Entity
#Table(name="PhoneOwner")
public class PhoneOwner {
#Id
#Column(name="owner_id")
long id;
#ElementCollection
#CollectionTable(name = "Phone", joinColumns = #JoinColumn(name = "owner_id"))
List<Phone> phones = new ArrayList<Phone>();
}
#Embeddable
class Phone {
#Column(name="type", nullable = false)
String type;
#Column(name="number", nullable = false)
String number;
}
Now, if you really don't want to introduce a table for the PhoneOwner, then you might be able to get out of it using a view. Like this:
create view PhoneOwner as select distinct owner_id from Phone;
As far as the JPA provider can tell, this is a table, and it will support the queries it needs to do to read data.
However, it won't support inserts. If you ever needed to add a phone for an owner who is not currently in the database, you would need to go round the back and insert a row directly into Phone. Not very nice.