I'm curious if it is possible to have several #ManyToOne relations of same entity in parent entity with JPA/Hibernate.
Example:
I have bank transactions, each transaction has a BankPartner in tow roles Creditor and Debtor. The point is, I want to edit data only once. BankPartner with nickName "mistress" is only one :), doesn't matter if in role creditor or debtor. Once, it will be renamed to wife, so I don't want to change separately. Also, the Balance is SUM of all transactions for BankPartner in both roles.
#Entity
public class Transaction {
..
#ManyToOne(fetch = FetchType.LAZY, cascade={CascadeType.ALL}, orphanRemoval=true, targetEntity = PartnerEntity.class)
#JoinColumn(name = "CREDITOR_ID")
private BankPartner creditor
#ManyToOne(fetch = FetchType.LAZY, targetEntity = PartnerEntity.class)
#JoinColumn(name = "DEBTOR_ID")
private BankPartner debtor
..
}
#Entity
public class BankPartner {
...
private String name;
private String nickName;
private String description;
...
}
I can imagine that from "Transaction" direction in can somehow work, but can't find a way to work from BankPartner direction.
I can see two different approaches
create #ManyToMany with "ROLE_TYPE" in intersection table between BankPartner and Transaction
create two separate entities Debtor an Creditor from the same table.
But, as I said, I'm curios about the first approach ..
You can use a multi column join as such:
#Entity
public class Transaction {
#ManyToOne(fetch = FetchType.LAZY, cascade={CascadeType.ALL}, orphanRemoval=true, targetEntity = PartnerEntity.class)
#JoinColumns({
#JoinColumn(name = "DEBTOR_ID"),
#JoinColumn(name = "CREDITOR_ID")
})
private BankPartner partner
}
Related
As the title says. Suppose I have the following entities:
#Entity
#Table
public class User {
#Id
private UUID id;
#Column(nullable = false)
private String name;
}
#Entity
#Table
public class Phone {
#Id
private UUID id;
#Column(nullable = false)
private String number;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
And I don't want to define a bi-directional association - that is, I don't want to define the relation on the owner side of the relation (in the User entity). Is there an easy way to mark a Phone entity for removal if I delete its parent User?
Looking for something like CascadeType.REMOVE but on the many side of the relation. Is there such a setting available?
Use cascade = CascadeType.DELETE and orphanRemoval = true in the owning side of your association.
#Entity
#Table
public class User {
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) // ALL includes DELETE
private List<Phone> phones;
...
}
Given we have the following entities that form a many-to-many relationship:
#Entity
public class A {
#Id
private Long id;
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(
name = "A_B",
joinColumns = #JoinColumn(name = "id_a"),
inverseJoinColumns = #JoinColumn(name = "id_b"))
private Set<B> listing;
}
#Entity
public class B {
#Id
private Long id;
}
I need to write a query that fetches B and applies some WHERE criteria on A side.
Since the relationsip is modeled from A entity's side it's very easy to write a query that joins these itsself:
new JPAQuery<>(entityManager)
.select(QB.b)
.from(QA.a)
.join(QA.a.listing,b)
.where(QA.a.name.eq("test"))
.fetch();
However since A_B table can be duplicated, this query can produce duplicate entries, which does not do for my scenario. So instead I need to start FROM B and JOIN A. And this is where I need help. I tried:
new JPAQuery<>(entityManager)
.select(QB.b)
.from(QB.b)
.join(QA.a).on(QA.a.listing.any().eq(QB.b))
.where(QA.a.name.eq("test"))
.fetch();
But that does not work as any() merely produces a subselect, instead of many to many join.
How do I write this query in Querydsl?
I have a question regarding ManyToOne relationship.
Assume I have 2 beans:
#Entity
#Table(name = "accounts")
public class Account {
#Id
#Column(name = "account_id")
private int account_id;
}
#Entity
#Table(name = "broker_account")
public class BrokerAccount {
#Id
#Column(name = "broker_account_id")
private int broker_account_id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="account_id", referencedColumnName = "account_id")
private Account account;
}
I am querying the entity below (plain get all query)
entityManager.createQuery("from BrokerAccount", BrokerAccount.class)
I thought that if I query BrokerAccount entity the account_id column will be populated by default on the Account object, since it exists in the BrokerAccount table as well, however all the Account fields are empty.
Am I missing something, should I define this field/column on the BrokerAccount entity itself as well to get its value?
You have defined the Account association as #ManyToOne(fetch = FetchType.LAZY). This means that while performing the entityManager.createQuery("from BrokerAccount", BrokerAccount.class), there will be no join on the Account and its data will not be fetched at that time.
In order to make the persistence provider fetch the Account data you would need to interact with the reference while being in the same transactional method, f.e.: brokerAccount.getAccount().getAccountId();
If you want to simply have a repeated column for the fk you can do:
#Column(name = "account_id", insertable=false, updatable=false)
private int account_id;
I have this three entities, that have their own table in a DB. The principal entities are Wearable and Job. Keep in mind that the CascadeType set in this code are wrong.
#Entity
public class Wearable {
#Id
private String mac;
#OneToMany(mappedBy = "wearable")
private List<Job> jobs;
#OneToMany(mappedBy = "wearable", cascade = {CascadeType.ALL})
private List<WearableSubscription> subscriptions;
}
#Entity
public class Job {
#Id
private String id;
#OneToOne(mappedBy = "job", cascade = CascadeType.ALL)
private WearableSubscription wearableSubscription;
}
Then I want to stablish a relation (WearableSubscription) between Wearable and a Job. Wearable can have many WearableSubscription but, only one of this WearableSubscription can have one Job.
#Entity
public class WearableSubscription {
private WearableSubscriptionId id;
#OneToOne (cascade = {CascadeType.ALL, CascadeType.PERSIST})
#JoinColumn(name = "job_id", insertable = false, updatable = false)
private Job job;
#ManyToOne(cascade = {CascadeType.ALL, CascadeType.PERSIST})
#JoinColumn(name = "wearable_mac", insertable = false, updatable = false)
private Wearable wearable;
private String message;
private String imageUrl;
private String audioUrl;
}
So at this point, I don't know exactly how CascadeType works. I mean, if I want to delete a WearableSubscription without deleting the Wearable and the Job, what types of Cascade I have to set in all of this fields? I'm trying all combinations and in some situations I delete all (WSub, W and J) and in others, when I perform the delete nothing occurs and all (WSub, W and J) stills at the DB.
As you can see on Cascade Type, you have a plethora of values you can pass on as an array to the cascade argument.
If all you want to NOT cascade are the DELETE operations, don't pass CascadeType.ALL (as it includes CascadeType.REMOVE) nor CascadeType.REMOVE.
Cascading occurs from an Entity which contains other entites. Since your WearableSubscription is a relationship entity you may not want to cascade operations made to it, as the changes are only relevant to the relation and not the entities themselves.
So, in your WearableSubscription class you want both Job and Wearable to have no cascade settings, and leave the other classes as is.
Hope this helps.
I have 2 java classes, Relation and Person, which both are present in my database.
Person:
#Entity
#Table(name = "persons")
public class Person {
#Id
#Column
private int id;
#Column
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "slave_id", referencedColumnName="id"),
#JoinColumn(name = "master_id", referencedColumnName="id")
})
private List<Relation> relations;
//Getters and setters
}
Relation:
#Entity
#Table(name = "relations")
public class Relation {
#Id
#Column
private int id;
#Column
private int child_id;
#Column
private int parent_id;
#Column
private String type;
//Getters and setters
}
Each Person has a list of relations (or not), the relation should be added to the list when the child_id or the parent_id of the relation is equal to the id of the person.
TL;DR:
When relation.child_id OR relation.parent_id = person.id => add relation to list of relations to the person
The issue I am facing is that this annotation:
#JoinColumns({
#JoinColumn(name = "child_id", referencedColumnName="id"),
#JoinColumn(name = "parent_id", referencedColumnName="id")
})
creates following SQL (just the necessary part):
relations relations6_
on this_.id=relations6_.slave_id
and this_.id=relations6_.master_id
What is the correct annotation in Java Hibernate to generate an SQL statement saying OR instead of AND
Some of the options that you could utilize:
Database views. Create the view that does custom join for you and map the entity to the view.
Join formula. I managed to make them work only on many-to-one associations. Nevertheless, you could make the association bidirectional and apply the formula in the Relation entity.
#Subselect. This is a kind of Hibernate view, suitable if you can't afford to create a real database view or change the db schema to better suit the entity model structure.
This and this answer could also be helpful.
Also, you can always use two separate associations for slaves and masters:
public class Person {
#OneToMany
#JoinColumn(name = "slave_id"),
private List<Relation> slaves;
#OneToMany
#JoinColumn(name = "master_id"),
private List<Relation> masters;
public List<Relation> getRelations() {
List<Relation> result = new ArrayList<>(slaves);
result.addAll(masters);
return result;
}
}
However, keep in mind that joining all of them in a single query requires full Cartesian product between masters and slaves.
You can use #FilterDef and #Filter annotations.