I have an JPA entity with contains a ManyToOne reference to another table, a simplified version of that entity is shown below:
#Entity
#Table(name = "ENTITIES")
public class Entity implements Serializable {
#Id #NotNull
private String id;
#JoinColumn(name = "REFERENCE", referencedColumnName = "ID")
#ManyToOne(optional = false)
private ReferencedEntity referencedEntity;
}
#Entity
#Table(name = "REFERENCES")
public class ReferencedEntity implements Serializable {
#Id #NotNull #Column(name = "ID")
private String id;
#Size(max = 50) #Column(name = "DSC")
private String description;
}
Finding entities works fine. Peristing entities also works fine, a bit too good in my particular setup, I need some extra validation.
Problem
My requirement is that the rows in table REFERENCES are static and should not be modified or new rows added.
Currently when I create a new Entity instance with a non-existing (yet) ReferencedEntity and persist that instance, a new row is added to REFERENCES.
Right now I've implemented this check in my own validate() method before calling the persist(), but I'd rather do it more elegantly.
Using an enum instead of a real entity is not an option, I want to add rows myself without a rebuild/redeployment several times in the future.
My question
What is the best way to implement a check like this?
Is there some BV annotation/constraint that helps me restrict this? Maybe a third party library?
It sounds like you need to first do a DB query to check if the value exists and then insert the record. This must be done in a transaction in order to ensure that the result of the query is still true at the time of insertion. I had a similar problem half a year back which might provide you with some leads on how to set up locking. Please see this SO question.
You should add this => insertable=false, updatable=false
And remove => optional=false , and maybe try nullable=true
Related
I'm having issues with defining a foreign key field within an entity. One specific thing that I can't find an answer to, is how to define such field but as a Long type, and not as that target entity type, and also set it up as ON DELETE CASCADE.
E.g.
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
and
#Entity
#Table(name = "address")
public class AddressEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JoinColumn(
table = "user",
name = "user_id",
referencedColumnName = "id")
private Long userId;
}
This example works fine, but now one can't easily define this DELETE ON CASCADE for the userId field i.e. Address entity.
One specific thing that I can't find an answer to, is how to define
such field but as a Long type, and not as that target entity type, and
also set it up as ON DELETE CASCADE.
It stands to reason that you cannot find an answer, because JPA does not provide one. If you want JPA to manage relationships between entities, then you must define those relationships in the JPA way, with entities holding references to other entity objects and declaring appropriate relationship annotations.* And if you want cascading deletes in your persistence context then you definitely do want them to be managed / recognized by JPA, for any other kind of approach is likely to create problems involving the context falling out of sync with the underlying data store.
It's unclear what problem you are trying to solve by avoiding JPA-style relationship management, but I'm inclined to think that there must be a better way. For example, if you want to avoid requiring the persistence context to load the associated UserEntity whenever an AddressEntity is loaded, then you would define the relationship with a lazy fetch strategy:
#Entity
public class AddressEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY)
private UserEntity user;
}
#Entity
public class UserEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL,
mappedBy = user)
AddressType address;
}
(Do note, however, that FetchType.LAZY is a hint, not a constraint. The context might sometimes still load the user together with its address if that's convenient.)
If you want to get the associated user id from an address, then the best way to do so is to read it from the user:
// ...
public Long getUserId() {
return (user == null) ? null : user.getId();
}
That does require the UserEntity to define an accessible getId() method, but since you are using JPA field-based access, you do not need also to provide a setter, and you may give the method default access. Or you could just declare UserEntity.id such that it is directly accessible by AddressEntity.
On the other hand, if you want to provide for the user ID to be accessible without loading the user entity then instead of a method such as the above getUserId(), in addition to the relationship field you could define a persistent, read-only AddressEntity.userId field, mapped to the appropriate column. It must be read-only because the value of the id in the underlying data store will necessarily be managed via the entity relationship, so it cannot also be managed via this separate field. For example:
#Entity
public class AddressEntity {
// ...
#OneToOne(optional = true, fetch = FetchType.LAZY)
private UserEntity user;
#Column(name = "user_id", insertable = false, updatable = false, nullable = true)
public Long userId;
}
This is a brittle approach, and I do not recommend it. It will be prone to problems with the userId field falling out of sync with the user entity. That may be bearable for the usage you have in mind, but this sort of weirdness is fertile ground for future bugs.
*Side note: as far as I know or can determine, JPA does not define semantics for a #JoinColumn annotation on a non-relationship field such as in your original code. That doesn't mean that your particular persistence provider can't interpret it in a way that you characterize as "works fine", but at minimum you are on thin ice with that.
I'm using PlayFramework 2.3 and i have the following classes:
MyEntity.java:
#Entity(name = "myentity")
public class MyEntity extends Model {
#Id
#GeneratedValue
public long id;
#OneToOne(cascade = CascadeType.ALL, optional = true)
#JoinColumn(name = "actual_version_id", nullable = true)
public Version actualVersion;
#OneToOne(optional = true)
#JoinColumn(name = "next_version_id", nullable = true)
public Version nextVersion;
...
}
Version.java
#Entity(name = "version")
public class Version extends Model {
#Id
#GeneratedValue
public long id;
#OneToOne
#JoinColumn(name = "entity_id", nullable = false)
public MyEntity entity;
...
}
When i want to make a new version for the entity, i copy it by detach, set the id to 0, and persist like this:
public Version clone(){
JPA.em().detach(this);
this.id = 0;
JPA.em().persist(this);
return this;
}
If i use the following code it works properly (first code)
entity.nextVersion = entity.actualVersion.clone();
JPA.em().flush();
entity.actualVersion = entity.nextVersion;
entity.nextVersion = null;
JPA.em().flush();
I didn't really like this code, because i could use it like this (second code)
entity.actualVersion = entity.actualVersion.clone();
JPA.em().flush();
But if I do this the foreign key doesn't update in the 'entity' table and i don't know why. Can anybody tell me what's the difference between the two implementtation of cloning? It seems some JPA black magic for me, but i couldn't find the answer.
EDIT:
This is a refactored code to make it easier to undestand. I'm not overriding any function from the Object class, or any other (my clone() function is called newRound in the original code with 2 parameters for example)
I don't really want to make any model modification like adding CascadeType.ALL to the annotation, or anything like that, because this is a program in production now, and i don't know what bugs would that make.
I just want to know why the first code updates the foreign key in the entity (actual_version_id) and the second doesn't. I think it has to be something with that CascadeType.ALL annotation parameter at the actualVersion variable.
Be very careful with clone() in a JPA setting; the JPA environment usually adds tracking properties to the bytecode of a class, and might get confused. Instead, override the default clone() to create an actual object and copy over all properties manually, one-by-one.
I have a JPA entity that contains collections of another entities instances.
I need to remove some of the instances from the collection and change other stuff, just for View and I don't want to change my database content.
What is the best way to do it?
Make a clone of my object and work with it.
Remove lazy load (or get all that I need from this main bean). Then close hibernate session, and work with the detached object.
Anything else?
UPDATE
My bean
#Entity
#Table(name = "client")
public class Client extends AbstractPersistentEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CLIENTS_SEQ")
#SequenceGenerator(name = "CLIENTS_SEQ", sequenceName = "clients_seq")
private Integer id;
#NotEmpty
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "clientId")
private Collection<ContactPhones> contactPhonesCollection;
}
And I want to remove some of ContactPhones for view. But it can be much complicated, may be in ContactPhones will be another collection and I want to remove it. Something like that.
If you don't want to actually remove any row from the database, in my opinion the best choice is to detach the entity from the session and work with it as with any other Java object.
I'm having two tables -
Foo { foo_id, name }
Foo_properties { fp_id, foo_id, phoneNumber}
Now I want to map this in my object model using hibernate..
I need a foo_id in Foo_properties because i want to maintain referential integrity and want to add ON DELETE CASCADE constraint.
so I mapped the relation in the following way -
#Entity
public class Foo{
#Id
private long foo_id;
private String name;
#OneToOne(mappedBy = "foo")
private FooProperties fooProperties;
}
#Entity
public class FooProperties{
#Id
private long fp_id;
private String phoneNumber;
#OneToOne
#JoinColumn(name = "foo_id", nullable = false)
private Foo foo;
}
Now since the owning side is FooProperties class, I'm facing following issues -
If I set the new instance of FooProperties to Foo the existing FooProperties still remains in DB and hibernate doesn't delete that instance, e.g.
Foo foo = entityManager.find(Foo.class, fooId);
foo.setFooProperties(new FooProperties("xxx-xxx-xxx"));
entityManager.merge(foo);
This results into the new row in FooProperties table along with the existing one. Now I don't understand how I can change my mapping to so I can have above code (or variant of it) working for all scenarios, that means I need Foo as a owning side and foo_id in FooProperties. Is there any way to define the mapping like this?
NOTE: I already asked question based on this but I think I wasn't clear in previous question so asked this another one.
You were already told to use orphanRemoval = true or CascadeType.DELETE_ORPHAN. However, due to casuistics in interpretation of JPA Specification it wouldn't work as expected for one-to-one relationships (HHH-5559).
You can achieve a proper behaviour of orphanRemoval with the following trick:
#Entity
public class Foo{
#OneToMany(mappedBy = "foo", orphanRemoval = true)
private List<FooProperties> fooProperties;
public FooProperties getFooProperties() {
if (fooProperties == null || fooProperties.isEmpty()) return null;
else return fooProperties.get(0);
}
public void setFooProperties(FooProperties newFooProperties) {
if (fooProperties == null) fooProperties = new ArrayList<FooProperties>();
else fooProperties.clear();
if (newFooProperties != null)
fooProperties.add(newFooProperties);
}
...
}
#Entity
public class FooProperties{
#ManyToOne
#JoinColumn(name = "foo_id", nullable = false)
private Foo foo;
...
}
Or even this, if you don't need FooPropeties.foo:
#Entity
public class Foo{
#OneToMany(orphanRemoval = true)
#JoinColumn(name = "foo_id", nullable = false)
private List<FooProperties> fooProperties;
// getter/setter as above
...
}
Bar is the owner of the association (as indicated by the mappedBy on the inverse side) and thus the cascade has to be set there.
Edit:
To invert that, this might help.
There are 2 options for you to choose from, since you don't want to change your mapping :
Do it via your service layer logic. I think you have a similar question already.
Use the Hibernate annotation #Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) on the Foo side of the relationship. However this is explicitly Hibenate and JPA 2 doesn't include support for the same.
I think instead of calling merge on the entity, if you directly call update on session object then hibernate will first delete the existing row and then it will add the new one. I implemented the same, but, in my case I used xml for mapping the entity. I hope this will help you.
I am using Hibernate and JPA. If I have two simple entities:
#Entity
#Table(name = "container")
public class Container {
#Id
#Column(name="guid")
private String guid;
}
#Entity
#Table(name="item")
public class Item {
#Id
#Column(name="guid")
private String guid;
#Column(name="container_guid")
private String containerGuid;
}
and I want to insure that inserting an Item fails if the referenced Container does not exist. I would prefer not to have a Container object populated inside the item object (ManyToOne), how would I do this if it is possible to do?
You can declare arbitrary constraint using columnDefinition attribute:
#Column(name="container_guid",
columnDefinition = "VARCHAR(255) REFERENCES container(guid)")
private String containerGuid;
Note, however, that Hibernate doesn't know anything about this constraint, so that, for example, it may not perform inserts in proper order with respect of it and so on.
Therefore it would be better to create a #ManyToOne relationship. If you are afraid of extra SQL query for Container needed to set this property, you can use Session.load()/EntityManager.getReference() to get a proxy without issuing actulal query.
Try using below relationship mapping
RelationShip Mapping
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#ManyToOne()
#ManyToMany()
<>
#JoinColumn(name="<>")