JPA deleting bidirectional association from inverse side - java

In my domain model there are a lot of bidirectional associations (both OneToMany and ManyToMany)
I've read this article and made all my associations on the basis of the sample pattern. (the ManyToMany associations has a two-sided addXY methods, following the pattern)
Using the pattern in this article the question is, what about deleting from the inverse side?
Example:
public class Customer implements Serializable {
...
#ManyToOne()
private CustomerStatus customerStatus;
#PreRemove
public void preRemove(){
setCustomerStatus(null);
}
public void setCustomerStatus(CustomerStatus customerStatus) {
if(this.customerStatus != null) { this.customerStatus.internalRemoveCustomer(this); }
this.customerStatus = customerStatus;
if(customerStatus != null) { customerStatus.internalAddCustomer(this); }
}
On the other side:
public class CustomerStatus implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(mappedBy="customerStatus")
private List<Customer> customers;
#PreRemove
public void preRemove(){
for(Customer c : customers){
c.setCustomerStatus(null); // this causes ConcurrentException
}
}
public List<Customer> getCustomers() {
return Collections.unmodifiableList(this.customers);
}
public void addCustomer(Customer c){
c.setCustomerStatus(this);
}
public void removeCustomer(Customer c){
c.setCustomerStatus(null);
}
void internalAddCustomer(Customer c){
this.customers.add(c);
}
void internalRemoveCustomer(Customer c){
this.customers.remove(c);
}
The problem is, that the preRemove method causes ConcurrentException. How to handle this?
The goal is, to delete the CustomerStatus, and set NULL all the Customers, where there was that status.
UPDATE
Without the preRemove method, I've got MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails

You cannot call this.customers.remove(c) while you are iterating over the customer collection. This question has come up before so you may find other solutions as in here:
How to avoid ConcurrentModificationException when iterating over a map and changing values?
but a simple solution is to just create a new list from the old to iterate over on preRemove:
public void preRemove(){
List<Customer> tempList = new ArrayList(customers);
for(Customer c : tempList){
c.setCustomerStatus(null);
}
}

Related

Java How To Avoid Type Casting

I have faced this problem a few times in the past, but haven't really found a good solution/design for it.
The below example code will generate PDF doc from Entity (Company or Article)
public class Entity
{
int id;
}
public class Company extends Entity
{
private String HQ;
}
public class Article extends Entity
{
private String title;
}
public interface EntityPDFGenerator
{
void generate(Entity entity);
}
public class ArticlePDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
Article article = (Article) entity;
// create Article related PDF from entity
}
}
public class CompanyPDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
Company company = (Company) entity;
// create Company related PDF
}
}
Main class:
public class PDFGenerator
{
public void generate(Entity entity)
{
EntityPDFGenerator pdfGenerator = getConcretePDFGenerator(entity);
pdfGenerator.generate(entity);
}
// lets make the factory task simple for now
EntityPDFGenerator getConcretePDFGenerator(Entity entity)
{
if(entity instanceof Article){
return new ArticlePDFGenerator();
}else{
return new CompanyPDFGenerator();
}
}
}
In the above approach the problem is with the casting the Entity to the concrete type (casting can be dangerous in later stage of the code). I tried to make it with generics, but then I get the warning
Unchecked call to 'generate(T)'
Can I improve this code?
Here, you go with the suggested changes:
public interface EntityPDFGenerator<T extends Entity> {
void generate(T entity);
}
public class ArticlePDFGenerator implements EntityPDFGenerator<Article> {
public void generate(Article entity)
{
// create Article related PDF from entity
}
}
public class CompanyPDFGenerator implements EntityPDFGenerator<Company> {
public void generate(Company entity)
{
// create Company related PDF
}
}
Short answer
Generics is not the right tool here. You can make the casting explicit:
public class CompanyPDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
if (! (entity instanceof Company)) {
throw new IllegalArgumentException("CompanyPDFGenerator works with Company object. You provided " + (entity == null ? "null" : entity.getClass().getName()));
}
Company company = (Company) entity;
System.out.println(company);
// create Company related PDF
}
}
Or you can define some sort of data structure in the entity class and use only that in the printer:
public abstract class Entity
{
int id;
public abstract EntityPdfData getPdfData();
}
// ...
public class CompanyPDFGenerator implements EntityPDFGenerator
{
public void generate(Entity entity)
{
EntityPdfData entityPdfData = entity.getPdfData();
// create Company related PDF
}
}
Long answer
Generics is useful if you know the types at compile-time. I.e. if you can write into your program that actual type. For lists it looks so simple:
// now you know at compile time that you need a list of integers
List<Integer> list = new ArrayList<>();
In your example you don't know that:
public void generate(Entity entity)
{
// either Article or Company can come it. It's a general method
EntityPDFGenerator pdfGenerator = getConcretePDFGenerator(entity);
pdfGenerator.generate(entity);
}
Suppose you want to add type to the EntityPDFGenerator , like this:
public static interface EntityPDFGenerator<T extends Entity>
{
void generate(T entity);
}
public static class ArticlePDFGenerator implements EntityPDFGenerator<Article>
{
public void generate(Article entity)
{
Article article = (Article) entity;
// create Article related PDF from entity
}
}
public static class CompanyPDFGenerator implements EntityPDFGenerator<Company>
{
public void generate(Company entity)
{
Company company = (Company) entity;
// create Company related PDF
}
}
This looks nice. However, getting the right generator will be tricky. Java generics is invariant. Even ArrayList<Integer> is not a subclass of ArrayList<Number>. So, ArticlePdfGenerator is not a subclass of EntityPDFGenerator<T extends Entity>. I.e. this will not compile:
<T extends Entity> EntityPDFGenerator<T> getConcretePDFGenerator(T entity, Class<T> classToken)
{
if(entity instanceof Article){
return new ArticlePDFGenerator();
}else{
return new CompanyPDFGenerator();
}
}
I would suggest to move the getGenerator() method in the Entity class and override it in the Company and Article classes.
Unless, of course, there is a good reason not to.

JPA persisting does not happen with relation when VARCHARS turn to CLOBs

I do have two entities that relate each other via a OneToMany-Relation.
Entity 1 is named "Change" and looks like the following
public class Change {
String attribute1;
#Column(name="\"ATTRIBUTE1\"")
public void getAttribute1() {
return this.attribute1;
}
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
// and 7 more of these....
List<ChangeTask> relatedChangeTasks = new ArrayList<ChangeTask>();
#OneToMany(cascade={PERSIST, MERGE, REFRESH}
#JoinTable(name="CHANGE_CHANGETASK", joinColumns={#JoinColumn(name="CHANGE_ID")}, inverseJoinColumns={#JoinColumn(name="CHANGETASK_ID")})
#JoinColumn(name="\"relatedChangeTask_ID\"" )
public List<ChangeTask> getRelatedChangeTasks() {
return this.relatedChangeTasks;
}
public void setRelatedChangeTasks(List<ChangeTask> relatedChangeTasks) {
this.relatedChangeTasks = relatedChangeTasks;
}
}
Entity 2 is named ChangeTask and extends Change.
public class ChangeTask extends Change {
// some additional attributes...
}
Persisting a new or existing Change record with one ChangeTask added to the "relatedChangeTask" list works just perfect.
Now I have to change the annotation of the 8 attributes from Default to #Lob, so Change now looks like this:
public class Change {
String attribute1;
#Lob
#Column(name="\"ATTRIBUTE1\"")
#Basic(fetch=EAGER)
public String getAttribute1() {
if(fieldHandler != null) {
return (java.lang.String) fieldHandler.readObject(this, "attribute1", attribute1);
}
return attribute1;
}
public void setAttribute1(String attribute1) {
if(fieldHandler != null) {
this.attribute1= (java.lang.String) fieldHandler.writeObject(this, "attribute1", this.attribute1, attribute1);
return;
}
this.attribute1= attribute1;
}
// and 7 more of these....
List<ChangeTask> relatedChangeTasks = new ArrayList<ChangeTask>();
#OneToMany(cascade={PERSIST, MERGE, REFRESH}
#JoinTable(name="CHANGE_CHANGETASK", joinColumns={#JoinColumn(name="CHANGE_ID")}, inverseJoinColumns={#JoinColumn(name="CHANGETASK_ID")})
#JoinColumn(name="\"relatedChangeTask_ID\"" )
public List<ChangeTask> getRelatedChangeTasks() {
return this.relatedChangeTasks;
}
public void setRelatedChangeTasks(List<ChangeTask> relatedChangeTasks) {
this.relatedChangeTasks = relatedChangeTasks;
}
}
Now, when I try to add a given ChangeTask to a Change the persist operation does not fail. But at the end of the Transaction the relation has not been persisted, meaning the relation-table "CHANGE_CHANGETASK" remains empty. When I debug through the whole process, I can see that the list contains one entry before "entityManager.merge()" operation and it still contains one entry after the merge. But it never arrives at the database.
Does anybody have an idea what I'm doing wrong here? As strange as it may sound, it must be something related with the #Lob annotations. As soon as I remove those again from the entity everything works fine.
Thanks in advance.
You wrote
public void getAttribute1() {
That can't be right. I think you mean
public String getAttribute1() {
Additionally you have annotated the setter:
#Column(name="\"ATTRIBUTE1\"")
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
You have to annotage either the field or the getter.

Hibernate/JPA: only one entry can have specific field value

I need something that seems not so specific but anyway I was unable to come up with nice and sophisticated solution.
Say I have very simple hibernate/jpa entity:
#Entity(name="entity")
public class Type {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique = true, nullable = false)
private String name;
#Column(unique = false, nullable = false)
private boolean defaultType;
}
What i need is to somehow annotate defaultType field so only (and exactly) one persisted entity have this value as true. When new entity get persisted with this defaultType as true, the old one (with defaultType=true) entity has to be altered and its defaultType value changed to false. Also if any entity get changed (its defaultType got changed to true), same rule should apply.
As far I know this can be achieved inside business logic (e.g. in DAO layer), with DB trigger or with hibernates interceptor or event (If there is another way, please let me know). I tried with DAO solution but it's kind of bad solution because it can be bypassed and it is really clumsy for such simple operation. DB triggers can not be added with hibernate/jpa annotations (if I am not mistaken) and i am not sure how to make this functionality with hibernate interceptors/events.
So, what is best solution for this problem?
You need use Callback method in JPA, for example PreUpdate or PostUpdate, for instance:
#Entity
#EntityListeners(com.acme.AlertMonitor.class) // set callback method in another class
public class Account {
Long accountId;
Integer balance;
boolean preferred;
#Id
public Long getAccountId() { ... }
...
public Integer getBalance() { ... }
...
#Transient
public boolean isPreferred() { ... }
...
public void deposit(Integer amount) { ... }
public Integer withdraw(Integer amount) throws NSFException {... }
#PreUpdate // callback method in some class
protected void validateCreate() {
if (getBalance() < MIN_REQUIRED_BALANCE)
throw new AccountException("Insufficient balance to open an
account");
}
#PostUpdate // callback method in some class
protected void adjustPreferredStatus() {
preferred =
(getBalance() >= AccountManager.getPreferredStatusLevel());
}
}
// callback method in another class
public class AlertMonitor {
#PreUpdate // callback method in another class
public void updateAccountAlert(Account acct) {
Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
}
}
Update: About your question, If I undestand what you want, this code may help you:
#Entity(name="entity")
#EntityListeners(com.yourpackage.TypeListner.class)
public class Type {
...
#Column(unique = false, nullable = false)
private boolean defaultType;
}
public class TypeListner {
pivate static Type objectWithTrue = null;
public void init() { // call this method when application is started
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(type.getDefaultType()) {
objectWithTrue = type;
}
}
}
private void changeDefaultType(Type changed) {
if(changed.getDefaultType()) {
if(changed != objectWithTrue && objectWithTrue != null) {
objectWithTrue.setDefaultType(false);
}
objectWithTrue = changed;
}
}
#PostPresist
public void newType(Type changed) {
changeDefaultType(changed);
}
#PostUpdate
public void updateType(Type changed) {
changeDefaultType(changed);
}
#PreRemove
public void removeType(Type changed) {
if(changed.getDefaultType() && objectWithTrue == changed) {
objectWithTrue = null;
}
}
OR
You can use listner #PreUpdate and #PrePresist and every times overwrite all Type objects without store any variable (it isn't so good for perfomance then first example, but more reliable):
#PreUpdate
void updateType(Type changed) {
if(changed.getDefaultType()
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(changed != type && type.getDefaultType()) {
type.setDefaultType(false);
}
}
}
}

Spring Data Neo4J 4 - no template.fetch()

I'm trying to upgrade from Spring Data Neo4J 3 to 4 - I'm using Neo4J 2.2.2.
I use a GraphRepository instance to query the database, fetching back an object.
This object has several relationships, which are not fetched (deliberately, to avoid reading in the entire graph).
In the SDN3 code, simply used the Neo4JTemplate class to perform a fetch call for each relationship I needed to fetch. This worked extremely well.
However, in SDN4 this facility has been removed, and replaced by various implementations of the load() method. It's not clear from the documentation how to achieve what I did in SDN3.
To be clear: if I have a Set of objects in the first class I retrieve, governed by a relationship, I want to retrieve only the objects in that Set, not the entire collection of those objects in the database.
Have I missed something crucial in the upgrade process, or is there a simple way of doing what I'm trying to do?
Adding code:
My entity class:
#NodeEntity
public class File implements MetroNode {
private Long id;
private String fileName;
private SourceState sourceState;
private Set<State> states;
#GraphId
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
#Relationship(type = "HAS_FILE", direction = Relationship.INCOMING)
public SourceState getSourceState() {
return sourceState;
}
public void setSourceState(SourceState sourceState) {
this.sourceState = sourceState;
}
public State addState(MetroNode otherNode, StateStatus status) {
if (this.states == null) {
this.states = new HashSet<State>();
}
State state = new State(this, otherNode, status.toString());
this.states.add(state);
return state;
}
#Relationship(type = "HAS_STATE", direction = Relationship.OUTGOING)
public Set<State> getStates() {
return states;
}
public State getActiveState() {
if (this.states != null) {
for (State state : this.states) {
if (state.isActive()) {
return state;
}
}
}
return null;
}
}
My repository class:
public interface FileRepository extends GraphRepository<File> {
File findByFileName(String fileName);
}
When executing the getActiveState() method I get a null return, because the states Set is empty (hasn't been fetched).
Looking again at my code, I wonder if it's because I'm not using a "native" load method from the repository, but the overloaded version?
SDN 4 allows you to control loading of related entities with the persistence horizon.
Loading an entity with depth 0 will fetch properties of the entity and no related entities.
Depth 1 will fetch the first level of related entities, but not their relations and so on.
Controlling the depth by relationship type is not supported.

PreUpdate not firing when adding to a collection [duplicate]

This question already has answers here:
JPA EventListener method not called on change to many-to-many collection?
(2 answers)
Closed 3 years ago.
I have a JPA annotated class which contains a collection like so:
#Entity
public class Employee {
#Id
private int id;
#Basic
private String name;
#OneToMany
#JoinTable(name = "ORG", joinColumns = #JoinColumn(name="MINION"),
inverseJoinColumns = #JoinColumn(name="EMP"))
private List<Employee> minions = new ArrayList<Employee>();
#PreUpdate
public void preUpdate(){ ... }
}
What I'm seeing is that if I have a managed Employee entity and I add to it's collection of minions the preUpdate method is not getting invoked. A new row is added to the mapping table in the DB so I know the update is going through. If I change a property directly on the Employee, like name, then preUpdate fires as expected when the transaction is committed.
Is there a way to get PreUpdate to fire when a mapped collection is modified? Or is there some other technique or Hibernate specific annotation for detecting when this happens?
#PreUpdate event is triggered just before database UPDATE operation is executed for the entity in question.
If you're not updating direct properties of Employee, there's no UPDATE to execute for its table and thus #PreUpdate listener is never called. You should have better luck using #PrePersist event which is triggered by "flush" rather than "update".
Maybe a this custom workaround works:
Create a subclass of ArrayList which identifies changes through ActionListener pattern
public class Employee {
....
private List<Employee> minions = createChangeNotifierList();
private List<Employee> createChangeNotifierList() {
ChangeNotifierList<Employee> l = new ChangeNotifierList<Employee>();
l.setActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
preUpdate();
}
});
return l;
}
public void setMinions(List<Employee> l) {
if (!(l instanceof ChangeNotifierList)) {
l = createChangeNotifierList();
preUpdate();
}
this.minions = l;
}
public void preUpdate(){ ... }
}
public class ChangeNotifierList<T> extends ArrayList<T> {
private ActionListener actionListener;
public ChangeNotifierList() {
}
public ChangeNotifierList(List<T> list) {
super.addAll(list);
}
public void setActionListener(ActionListener actionListener) {
this.actionListener = actionListener;
}
public boolean add(T e) {
boolean b = super.add(e);
if (b) {
notifyChange();
}
return b;
}
private void notifyChange() {
actionListener.actionPerformed(null);
}
.....
}
Here is my implementation for Hibernate provider:
http://pastebin.com/8cPB96bZ
Generally you just mark methods that should be called in the case of a dirty collection with #PreCollectionChange annotation.

Categories

Resources