Having the following entity:
#Entity
class Transaction implements Serializable {
#Id #GeneratedValue
Long id
#Column(nullable = false, updatable = false)
Long trNumber
#OneToMany(mappedBy = "transaction", cascade = CascadeType.ALL, orphanRemoval = true)
Set<Entry> entries = new HashSet()
#Column(updatable = false, nullable = false)
#Temporal(TemporalType.TIMESTAMP)
Date creationDate
}
I'd like entries to be impossible to update but #OneToMany doesn't allow the updatable = false attribute.
Here is the Entry entity:
#Entity
class Entry implements Serializable {
#Id #GeneratedValue
Long id
#ManyToOne(cascade= CascadeType.ALL)
#JoinColumn(updatable = false, nullable = false)
Transaction transaction
}
I can use a JPA listener to throw an exception every time we try to update the Transaction but I'd like to set the updatable=false behavior at the entity level, as I do for the other attributes.
I guess that you can't achieve it using annotations in JPA 2.0. I've read in "Pro JPA 2" book from Apress that such read-only relationships are discussed for future versions of the specification.
I've had a similar problem which described here. I've also asked the Expert Group for any comment here (at the time of writing this post - no answer was given).
The cascading doesn't change a bit as you don't have to "merge" objects in order to change its state. You just can fetch the Entry which is moved to the managed state (it also makes its Transaction to be moved to the managed state), so each update must be reflected, at the end, in the database.
The #JoinColumn(updatable=false) seems to mean just that you cannot change the Transaction entity to something else - not that you cannot change its state.
You could detach the Transaction entity as soon as you fetch the Entry. In this case changes made to Transaction will be saved (it's managed) but to Entry won't (it's detached). You'd just need to remember to refresh the state of your Entry at the end, as your in-memory representation could be not equal to the database one.
Related
I have an entity with reference to another one by FK, at the same time I have a field mapped on the same column to have access right to the identifier, let's say
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue
Long id;
#JoinColumn(name = "author_id")
#ManyToOne(fetch = FetchType.LAZY)
Author author;
#Column(name = "author_id", insertable = false, updatable = false)
Long authorId;
}
so, for now on select via JPA repository (findById for instance) the field of "authorId" is always null, but in actual database it exists and object of "author" fills correctly. Tested in the transaction and outside - result is the same.
About app - it is spring boot 2.2.8 with spring data
Are there any ideas where I can be wrong?
*Update: found the reason - all the found entities are cached somehow, after detaching them from persistence context all data loads as expected. Seems it's clear, but still cant get where interactions with these entities appear, obviously not in my tx - it is pretty small and simple. Never thought that neighboring transactions can affect cache this way =((
I'm currently trying to persist data which I'd stored as a Map in Java.
I've gotten this to work using List already, however when I try to make this a Map instead using #MapKeyEnumerated and #MapKey it acts a bit unexpected.
ExampleData.java
#MapKey(name = "feature")
#MapKeyEnumerated(EnumType.STRING)
#OneToMany(targetEntity = ExampleFeature.class, mappedBy = "exampleData")
private Map<Feature, ExampleFeature> features;
ExampleFeature.java
#ManyToOne
#JoinColumn(name = "example_id", nullable = false)
private ExampleData exampleData;
#Enumerated(EnumType.STRING)
#Column(name = "feature", nullable = false)
private Feature feature;
I'm able to add rows to the database manually outside of runtime, and when running the application it's able to load those entities correctly. All other entities are also working as intended.
When persisting entities in from the application, the ExampleFeature does appear in the Map, but all properties in the instance are the default Java values, and after stepping-out
Dependencies
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-c3p0:5.4.17.Final
Could anyone point me in the right direction as to what I might have wrong here?
The issue is that in the project, #save() was only called on ExampleData, but not on any of the relational properties under it. This means that Hibernate would try to persist the ExampleData instance, however not any nested properties such as the EnumMap.
The solution is to set cascade = CascadeType.ALL which will tell Hibernate to persist changes in relational entities such as ExampleFeature when persisting ExampleData.
ExampleData.java
#MapKey(name = "feature")
#MapKeyEnumerated(EnumType.STRING)
#OneToMany(targetEntity = ExampleFeature.class, mappedBy = "exampleData", cascade = CascadeType.ALL)
private Map<Feature, ExampleFeature> features;
I'm working with JPA 2 + Hibernate 4 and I'm implementing some CRUD operations on model entities.
Now I need to prevent a certain entity (EntityB) to be deleted when a related entity (EntityA) exists in database:
#Entity
public class EntityA {
#Id
private int id;
#OneToOne(mappedBy = "entityA", optional = false, fetch = FetchType.LAZY)
private EntityB entityB;
//...
}
#Entity
public class EntityB {
#Id
private int id;
#OneToOne
#JoinColumn(name = "id")
private EntityA entityA;
//...
}
Is there any way to achieve this using relationship options or should I check EntityA existence in my dao/repository before removing EntityB?
NOTE I need this also for #ManyToOne relationships.
If you want to prevent that in your code, than simply do not delete that entity (by checking that manually). There is no possibility to do that with annotations.
On the other side, this sounds to me rather like a need for a DB constraint. If those entities are already related, then simply add a foreign key constraint (if none is existent). If not, than think of adding one.
PS: if you already have a relationship, check the CascadeType.REMOVE setting.
I don't think you can solve this with annotations. You should manally check related-entity existence before.
I have a JPA Entity that has an attribute declared in the following way
#OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinColumn(name = "userId", referencedColumnName = "id", insertable = false, updatable = false)
private UserBare user;
(UserBare is a dummy entity I've created which is a dumbed down version of a User obj as I needed some date, not all of User obj be made available to this entity. Is there a better way to approach this?
This is a convenience attr that I use just make the userBare object available to this entity when reading this entity. But when I actually write this entity, it seems to create new entries of 'user' in the database (instead of updating). I already have insertable=false,updatable=false but it still writes to the database. I tried removing the CascadeType declaration but that is throwing an error.
Here is the database snapshot after the unwanted rows are added (last 3). Also I've noticed that deleting the original entity did not delete these three unwanted rows. So I guess JPA is storing them but the references are intact to the original entity.
My souspicioun would be that "OneToOne" means "OneToOne" excluding the possibility of "OneToZero" if you don't set Optional to true like this:
#OneToOne(optional=true)
But just a guess.. I'm not sure
I'm mapping a database (Oracle 11g) to JPA entities using EclipseLink. I have mapped almost every table to JPA objects but I've just found a problem:
In the \Curve\ entity I have the following fields:
#Id
#Column(name = "COD_CURVE")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq.gen")
#SequenceGenerator(name = "seq.gen.curve", sequenceName = "SEQCURVE", allocationSize = 1)
private long codCurve;
#Id
#Column(name = "FEC_HISTORIC")
#Temporal(javax.persistence.TemporalType.DATE)
private Date fecHistoric;
#OneToMany(mappedBy="codCurve", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<CurveDetail> detailsCollection;
In the \CurveDetail\ entity I have this:
#ManyToOne
#JoinColumns({
#JoinColumn(name = "COD_CURVE", referencedColumnName = "COD_CURVE"),
#JoinColumn(name = "FEC_HISTORIC", referencedColumnName = "FEC_HISTORIC")
})
private Curve codCurve;
The problem is that when I query the \Curve\ entity, the details always are null, despite the fact that there is valid data in both tables. Checking the database I've noticed that there are no foreign key constraints in the \CurveDetail\ table, so I wonder ¿Are these constraints required to map the database correctly? I haven't tried to add the FK constraint myself because I'm not allowed to (have to ask a DBA to do it, and it'll take a week).
Thanks in advance!
Having a foreign key is not required.
Check the SQL that is generated by enabling logging ("eclipselink.logging.level"="finest")
Try executing the same SQL with the same database to see if the data exists.
Also ensure you are not corrupting the shared cache by inserting/updating an object with a null collection. You must maintain both sides of a bi-directional relationship. You could try disabling the cache to see if this is what you are doing.
As I know having a FK constraint is not mandatory. Once I had the same problem (but it was Oracle 9i) and found out that the name of Entity class should match the name of the table you want to map, and also JPA SQL and Hibernate HQL are both case sensitive so be careful in writing #Column tag.