JPA, change field value using java on UPDATE/merge - java

I have an entity with a field updatedUser which keeps track of the user who updated the row values.
When the entity is created this field is null but it should be set when a change is made to the entity and merge is used.
Is there some way, through java, to fill this value only when the entity has been updated? IE: should not be changed if it is created or retrieve from db.
#Entity
#Table(name="employees", uniqueConstraints= {
#UniqueConstraint(columnNames="idEmployees"),
#UniqueConstraint(columnNames="idCardNumber"),
#UniqueConstraint(columnNames="niNumber")
})
public class Employee {
#Id
#GeneratedValue
#Column(unique=true, nullable=false, updatable=false)
private int idEmployees;
//other class variables
#ManyToOne(cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
#JoinColumn(name="updatedEmployeeId")
private Employee updatedEmployee;
//constructors, getters and setters
}

Updating a value is possbile by using the javax.persistence.PreUpdate annotation. But there is no way to inject the current user. Maybe it is possible to read the user from ThreadLocal and then set the user, but that is not a really clean solution as it must be set before when entering your business code or so ..

Related

Increment #version when an entity is embedded in an #embeddable

I know there is a similar question listed here but in my own case, I don't have the dates across . Rather, I have an embedded class with an entity in it.
I have the below entity definitions:
Person JPA Entity
#Entity
#EntityListeners(AuditingEntityListener.class)
public class Person {
#Id
private int id;
#CreatedDate
private OffsetDateTime creationDate;
#LastModifiedDate
private OffsetDateTime updatedDate;
#Version
private Long version;
#Embedded
private ContactAddress contactAddress;
}
ContactAddress JPA Entity
#Embeddable
public class ContactAddress {
private String notice;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "former_street_id")
private Street formerStreet;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "new_street_id")
private Street newStreet;
}
Street JPA Entity
#Table(name = "p_street")
public class Street {
#Id
private int id;
private String line1;
private String line2;
}
The Street JPA entity is a field of the ContactAddress class. When I update a ContactAddress instance, the version in the Person entity is incremented provided it is the 'notice' field of the ContactAddress that is changed. If however, I update any of the Street Object fields, the version is not incremented.
The reason being that I am definitely missing how to notify the auditingEntityListner to raise an event on the updateDate field on the Person entity.
It appears, with
me having the Street entity in the embedded ContactAddress, a disconnect has been created between the Person entity and the Street entity. Is there a way to fix this in JPA?
It works as expected becauyse when you modify Street, you are not modifying, only some entity in different table. Person (person table) itself is unchanged thus no increment in version.
When you modify ContactAddress, since it is a "part of Person" (literally a set of columns in person table - thus person), you are modifying row in person entry, therfore version is bumped up.
If you need to version to bumped up (I would rather not) you have to "touch" Person when you are modifying Street entity. I dont know if there is an auto way of doing it.
Keep in mind, that your intention is to potentially increment version of multiple person entries when you modify address as address might be used in multiple person entries.
If you want to ensure that the #Version field is increased then:
#PersistenceContext
private EntityManager em;
public void savePerson(Person p) {
em.lock(p, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
}
This way the person's version field is always increased.
Note that the version field is increased by two if the Envers framework is aware of an change, e.g. when notice has been modified.

Relationship table mapped as entity in JPA

I'm trying to map one specific many to many table on my database as an entity in JPA (cause I have some specific attributes on my relationship table and I wanted to retrieve this as the class attributes two). But having issues while declaring the IDs.
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Entity
#Table(name = "user_plan")
public class UserPlan implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#OneToOne
private User user;
#Id
#OneToOne
private Plan plan;
private Integer billingDay;
#Enumerated(EnumType.STRING)
private BillingType billingType;
#Enumerated(EnumType.STRING)
private PlanStatus planStatus;
}
The application starts successfully but when I try to map some repository to manage this table, Hibernate throws an error:
java.lang.IllegalArgumentException: This class [class com.demo.domain.model.UserPlan] does not define an IdClass
How can I use the JPA entity annotation to manage this relationship table? Is it possible?
I cannot simply declare one property in the user class of Plan model and mark it as #ManyToMany, cause the plan table does not have the property that I need to execute some operations, which are declared on UserPlan class, also I cannot move these properties to Plan class, cause the Plan table is just a template of a plan, and the UserPlan have all the specific data (billingDay, billingType and planStatus).
JPA supports relationship tables as a Java class? Or it can be mapped only as a property?
Thanks
You are using multiple #Id annotations. To do so you need to create PrimaryKey class:
public class PrimaryKey implements Serializable {
private User user;
private Plan plan;
// Getter and Setter
}
And you need to add #IdClass(PrimaryKey.class) annotation to your entity class.
If you have a Repository don't forget to change id type to PrimaryKey:
public interface YourRepository
extends SomeRepositoryInterface<UserPlan, PrimaryKey> {
//...
}
Also check this question

Separate Table vs Extra Columns in JPA/Hibernate

I am wondering about best practices in database design with Hibernate.
I have a User entity that is going to have a lot of different settings. For each set of settings, I have to either add them as extra columns in the User table or create a separate entity and connect them with a #OneToOne relationship. It is my understanding that #OneToMany and #ManyToOne relationships should generally take place in separate tables because you should not have columns that are optional.
But it is kind of unclear for #OneToOne relationships. I think there is a case for using #OneToOne because ORMs will select all single attributes by default and having a lot of columns will slow down that process.
An example of what I am talking about can be illustrated by
#Entity
public class User{
#OneToOne
private ForumSettings forumSettings;
#OneToOne
private AccountSettings accountSettings;
#OneToOne
private SecuritySettings securitySettings;
}
vs
#Entity
public class User{
#Column
private boolean showNSFWContent; //Forum Setting
#Column
private int numberOfCommentsPerPage; //Forum Setting
#Column
private boolean subscribedToNewsLetter; //Account Setting
#Column
private boolean isAccountBanned; //Account Setting
#Column
private boolean isTwoFactorAuthenticationEnabled; //Security Setting
#Column
private boolean alertForSuspiciousLogin; //Security Setting
}
The above is a simple example to show the concept, but in practice there would be many more columns in the 2nd portion.
I know that this might be opinion based, but I am hoping someone could share the pros/cons of both choices.
Thank you very much
Your question is in general about Data normalization. Normalization is itself extensive field of study and basically is a way of structuring database tables avoiding redundancy and making sure that updates don’t introduce anomalies.
And first rule of normalization says a table shall contain no repeating groups. In your case it does.
SOLUTION 1 : Store UserSettings as Entity as map as OneToMany relationship
#Entity
public class User
#OneToMany
private List<UserSettings> userSettings;
And then you can query for particular setting type by joining User and UserSettings entities.
For example (JPQL)
SELECT user u
JOIN u.settings us
WHERE us.settings_type = 'account_settings'
and us.settings_value = 'secure' // or any other logic
Advantage of this approach is that UserSettings will have it is own persistence identity and can be queried by it's own. It it is not dependent on parent.
For example :
SELECT q from Query q where ...
Solution 2 : Store settings in a collection of basic elements
You can store User Settings in the collection (Each user will have it's own set of settings)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#ElementCollection
#CollectionTable(name="USER_SETTINGS")
#MapKeyColumn(name="SETTINGS_TYPE")
#Column(name="SETTINGS_VALUE")
Map<String, Boolean> userSettings = new HashMap<>();
UserSettings collection will be stored in a separate table with foreign key to User table. UserSettings does not have it is own persistence ID, is dependent on User entity and can be queried only through it is parent ('User')
Solution 3: Store User Settings as Embedded type
Embedded type is not an entity, it does not have it is own persistence ID and is depends on parent type, stored as part of parent record in database (in User table)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#Embedded
private UserSettings userSettings;
UserSettings is in separate class, but stored in User table.
#Embeddable
public class UserSettings {
private List<String> securitySettings; // or any other collection type
private List<Boolean> forumSettings;

#OneToOne field value not loaded after save

I have a Spring Data JPA Project, my entity is the following
#Entity
#Table(name = "delivery_offers")
public class DeliveryOffer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
#JoinColumn(name = "DELIVERY_ID")
private Delivery delivery;
... other fields
The table also has a foreign key for DELIVERY_ID which references to DELIVERY.ID
My Dao is a JpaRepository object, I try to persist a DeliveryOffer with dao.saveAndFlush (deliveryOffer).
I also set delivery.id = xy
I would like to return the persisted DeliveryOffer object, so I make in my controller
return dao.saveAndFlush(deliveryOffer)
The problem is, that in the returned DeliveryOffer the referenced Delivery object fields are all null except of the id, which I set for deliveryOffer.delivery.id (the Delivery exists in the DB with the given id)
If I try to get an "older" deliveryOffer from a recent transaction with findOne, the referenced delivery is then filled correctly.
So I suppose, it has to do something with the transaction. I thought, saveAndFlush commits it immediately, so in the return object I would await the correct entity.
Do you have an idea, why the delivery is empty at this point?

Hibernate Annotations Java Map

I need some help with hibernate annotations.
I have the following 2 entities:
public class Custom {
private Map<KeyObject, ValueObject> properties;
#oneToMany(mappedBy = "customId", cascade = CascadeType.All)
#MapKey(name = "keyObject")
public Map<KeyObject, ValueObject> getProperties();
.....
}
public class ValueObject {
private KeyObject keyObject;
private Long customId;
private String value;
...getters and setters
}
I have a simple dao class to save, update, select, and delete records.
If I remove a record from the map in the Custom object and then call customDao.save(custom) my changes are never persisted to the database. It is not throwing any errors either.
I saw in the hibernate examples I can define the customId as a Custom object instead of a Long in the KeyValue table but I did not want to do this.
Am I setting up my entity's incorrectly?
When an association has the mappedBy attribute, it means: I'm not the owner of this association. Any change done to this side of the association won't matter for Hibernate. What will matter is the other side of the association.
I'm surprised this even works, because I would have expected Hibernate to require a ManyToOne on the other side, of type Custom. But what's sure is that if you don't set the custom/customId field to null in KeyValue/ValueObject, Hibernate won't set this column to null.

Categories