I've seen other posts about this problem, but have found no answer to my own troubles. I have
#Entity
#Table(name= ServerSpringConstants.COMPANY)
public class Company implements Serializable {
private static final long serialVersionUID = -9104996853272739161L;
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column (name = "companyID")
private long companyID;
#OneToMany (targetEntity = Division.class, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH},
fetch = FetchType.EAGER)
#JoinTable (name = "companyDivisionJoinTable",
joinColumns = #JoinColumn(name="companyID"),
inverseJoinColumns = #JoinColumn(name="divisionID")
)
private Set<Division> divisions = new HashSet<>();
public long getCompanyID() {
return companyID;
}
public Set<Division> getDivisions() {
return divisions;
}
public void setDivisions(Set<Division> divisions) {
this.divisions = divisions;
}
}
On the other side:
#Entity
#Table(name= ServerSpringConstants.DIVISION)
public class Division implements Serializable {
private static final long serialVersionUID = -3685914604737207530L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
#Column(name = "divisionID")
private long divisionID;
#ManyToOne
(fetch = FetchType.LAZY, optional = false, targetEntity = Company.class,
cascade = {CascadeType.PERSIST, CascadeType.MERGE
}
)
#JoinColumn(name="companyID", referencedColumnName = "companyID")
private Company company;
public long getDivisionID() {
return divisionID;
}
public void setDivisionID(long divisionID) {
this.divisionID = divisionID;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Yet for some reason, LAZY loading not working. I'm using JPA. I'm calling back the companies, and their enclosing divisions from within a 'User' class -- the pertinent part
#ManyToMany (targetEntity = Company.class,
cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH},
fetch=FetchType.EAGER )
#JoinTable (
name="companyUserJoinTable",
joinColumns=#JoinColumn(name="userID"),
inverseJoinColumns=#JoinColumn(name="companyID")
)
private Set<Company> company = new HashSet<>();
I've searched out existing threads, and have tried adding various suggestions, but nothing has helped!
Any help appreciated.
Thanks!
Since you are loading the divisions set eagerly (with fetch = FetchType.EAGER) and you have a bidirectional association, divisions will be initialized with the parent reference to company. I can't see any problem with it. Jpa loaded the full object tree because you just told it so. A company contains divisions which contain a back reference to the company that loaded them.
To understand it better, since the reason for lazy loading is to reduce the data loaded from database, the owning company is already loaded in session for the divisions, so why not setting the association too?
The #ManyToOne association on the other side takes effect if you load divisions first.
To be more correct with your mapping add also a #MappedBy attribute to the one part of the association. This does not affect fetching behavior but will prevent double updates to the database issued by both ends of the association.
Related
Repost from here
Given entities and repository:
#Entity
public final class Partner {
#Id
private String id;
#OneToMany(mappedBy = "partner", fetch = FetchType.LAZY)
private List<Merchant> merchants;
...
}
#Entity
public final class Merchant {
#Id
private String id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private Partner partner;
...
}
public interface PartnerRepository
extends JpaRepository<Partner, String>, QueryDslPredicateExecutor<Partner> {
}
If there is only one partner having two merchants in the DB then the following code incorrectly returns list with two instances of the same parnter.
partnerRepository.findAll(new Sort("merchants.name"));
This is caused internally by the DB join. By creating custom implementation that adds the distinct to the selection the result is correctly the single partner.
Wouldn't it be correct to do distinct selection per default?
Try
#OneToMany(mappedBy = "partner", fetch = FetchType.LAZY)
#OrderBy("name")
private List<Merchant> merchants;
I have a OneToMany Relationship (User to EmailAddress)
Maybe I'm going about this the wrong way, My Database is empty but If I want to POST a User object and add it to the Database, along with the emailAdresses object and have the EmailAddress persisted also.
I want 2 records in the Database:
1 User and 1 EmailAddress (with a fk to User table)
Service Class
Currently what I've implemented to get this to work is this:
#Service
public class UserService {
private UserRepository userRepository;
private ModelMapper modelMapper;
public UserService(UserRepository userRepository, ModelMapper modelMapper) {
this.userRepository = userRepository;
this.modelMapper = modelMapper;
//Used for mapping List
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
.setSourceNamingConvention(NamingConventions.JAVABEANS_MUTATOR);
}
public User createUser(UserCreateDTO userCreateDTO) {
User user = modelMapper.map(userCreateDTO, User.class);
//persist User to EmailAddress object
if(user.getEmailAddresses() != null){
user.getEmailAddresses().forEach(user::persistUser);
}
return userRepository.save(user);
}
public UserDTO getUserById(Long id) {
User found = userRepository.findById(id).get();
return modelMapper.map(found, UserDTO.class);
}
// .....
Which I have seen used in some bidirectional relationships
User Entity
#Entity
#Table(name = "Users")
#Getter #Setter #ToString #NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", updatable = false, nullable = false)
private Long id;
private String firstName;
private String lastName;
private int age;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<EmailAddress> emailAddresses;
Email Address Entity
#Entity
#Table(name = "Email")
#Getter #Setter #ToString #NoArgsConstructor
public class EmailAddress {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="email_id", updatable = false, nullable = false)
private Long emailId;
private String email;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST )
#JoinColumn(name = "user_id", nullable = false)
#JsonIgnore
private User user;
Is there a better way to set up the Join relationship?
Sample POST Request
{"firstName":"Joe", "lastName":"Bloggs", "age": 30, "emailAddresses" : [ "joe-private#email.com" , "joe-work#email.com" ] }
I guess you need to associate this email with a user as well, not just set user to email entity.
public void persistUser(EmailAddress emailAddress) {
// set this email to the user
// if email EmailAddresses list is null you might need to init it first
this.getEmailAddresses().add(emailAddress);
emailAddress.setUser(this);
}
Firstly, I believe that a method persistUser should not be a part of a service layer - due to its implementation it mostly like a Domain layer method that should be implemented within a User entity class.
Secondly, since it's a POST method you shouldn't care of emails existence - you are adding a new user with a new set of emails
Closer to a question, I'd suggest you to try this:
public class UserService {
/************/
#Autowired
private UserManager userManager;
public void addUser(UserModel model) {
User user = new User(model);
List<EmailAddress> emails = model.getEmailAddresses().stream().map(EmailAddress::new).collect(Collectors.toList());
user.setEmailAddresses(emails);
userManager.saveUser(user);
}
}
and at the User add this:
public void setEmailAddresses(List<EmailAddress> emails) {
emails.forEach(item -> item.setUser(this));
this.emailAddresses = emails;
}
And don't forget to implement constructors for entities with model paremeters
I have two Entities: Box and Link.
(Both inherit from _BaseClass, but I do not think that's relevant - it might be though....)
So a Box contains link1, link2, and a Collection of Links.
_BaseEntity:
#MappedSuperclass
public class _BaseEntity implements Comparable<_BaseEntity> {
#Expose //
#Id //
#GeneratedValue() //
protected long id;
public _BaseEntity() {}
public long getID() {
if (id == 0) return creationId;
return id;
}
#Override public final int hashCode() {
return (int) getID();
}
#Override public final boolean equals(final Object pObj) {
if (pObj == null) return false;
if (getClass() != pObj.getClass()) return false;
final _BaseEntity other = (_BaseEntity) pObj;
return id == other.id;
}
#Override public int compareTo(final _BaseEntity arg0) {
return (int) (getID() - arg0.getID());
}
}
Box:
#Entity
#Table(name = "PT_Box")
public class Box extends _BaseEntity {
#Expose private String name;
#Expose //
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "parent") //
private Link link1;
#Expose //
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "parent") //
private Link link2;
#Expose //
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "parent") //
private final ArrayList<Link> links = new ArrayList<>();
}
Link:
#Entity
#Table(name = "PT_Link")
public class Link extends _BaseEntity {
#ManyToOne(fetch = FetchType.EAGER) //
#JoinColumn(name = "parent_id") //
private final Box parent; // dont expose for not looping!
#Expose private String name;
#Expose private String link;
#Expose private Date lastUpdate;
#Expose private Date nextUpdate;
}
Problems:
The Links get messed up. 'link1' and 'link2' show the first element of 'link'.
And vice versa: if I set 'link1', then the list 'links' will show it as first element.
Suspicion:
I am sure, that is due to the mappings
Box: #OneToMany(mappedBy = "parent")
Link: #ManyToOne #JoinColumn(name = "parent_id")
that they also link those links into the variables 'link1' and 'link2'.
Question:
So my question: how to manage/annotate this properly?
Un-map 'link1' and 'link2' somehow?
Annotate 'link1' and 'link2' as #Transient, so that the references only get set by the Link-side? (if that works at all...)
Your mapping is incorrect.
First of all, the hashCode and equals() methods should not use the generated ID. You should probably not have any equals or hashCode method. That's the safest way (see http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode)
Second, the collection must be of type List, not ArrayList.
Third: you need three different join columns( and thus three different parent fields) in the Link entity:
one to know what box contains the link as its link1 (OneToOne, owner side of the OneToOne link1 association)
one to know what box contains the link as its link2 (OneToOne, owner side of the OneToOne link2 association)
one to know what box contains the link as one of the elements of its links list (ManyToOne, owner side of the OneToMany links association)
the old issue description is obsolete
#unwichtich thank you for your tip, it helped get rid of that nasty error.
I have the entities:
#Entity
#XmlRootElement
#Table(name="WAITERENTITY")
public class WaiterEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.EAGER)
#JoinColumn(name = "waiter_id")
private List<OrderEntity> orders = new ArrayList<>();
{plus usual setters and getters}
}
and
#Entity
#XmlRootElement
#Table(name="ORDERENTITY")
public class OrderEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long orderNumber;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "table_id")
private TableEntity table_;
private int sumOfMoney = 0;
private boolean finalized = false;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.LAZY)
#JoinColumn(name = "order_id")
private List<OrderItemEntity> orderItems = new ArrayList<>();
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "waiter_id")
private WaiterEntity waiter;
{plus usual setters and getters}
}
But the main problem remains. In the database, everything is as it should be:
<waiterEntities>
<waiterEntity>
<id>9</id>
<name>Jack Marston #499</name>
</waiterEntity>
<waiterEntity>
<id>10</id>
<name>Abigail Marston</name>
</waiterEntity>
</waiterEntities>
<orderEntities>
<orderEntity>
<finalized>false</finalized>
<orderNumber>12</orderNumber>
<sumOfMoney>0</sumOfMoney>
<waiter>
<id>9</id>
<name>Jack Marston #499</name>
</waiter>
</orderEntity>
</orderEntities>
But the #OneToMany relation of WaiterEntity does only return an empty list when waiter.getOrders() is called.
The method that creates a new OrderEntity is the following:
public void create(OrderEntity e) {
WaiterEntity waiter = em.find(WaiterEntity.class, e.getWaiter().getId());
if (waiter != null) {
(1) e.setWaiter(waiter);
em.persist(e);
System.out.println("after persist:\n" + e);
(2) //waiter.getOrders().add(e);
(3) //em.merge(waiter);
}
}
Edit: I observed very strange behaviour. Firstly, if the lines marked with (2) and (3) are un-commented, no OrderEntity will be persisted at all. Secondly, only the following outside statements will suffice GlassFish to persist an OrderEntity:
WaiterBean waiter = client.findByNameSingle(WaiterBean.class, "John Marston");
client.create(new OrderBean(waiter));
Where create will get an unique id of the respective WaiterEntity from the database. On the other hand, an OrderEntity will be not persisted, if no WaiterEntity id is known, as in for example:
client.create(new OrderBean(new WaiterBean("Hans")));
because this new object is not obtained from the database. The strage behaviour appears, when line marked with (1) is commented out: the first statement, with the previous obtainment of the respective WaiterEntity from the database won't work, but the second statement, that doesn't obtain any WaiterEntity from the database, will work and create an OrderEntity entry in the database. I really have a hard time understanding that.
The two commented lines (2) and (3) should assure that the WaiterEntity knows its OrderEntitys for later retrieval. But the only thing that these two lines do (or one of them, i tried that as well) is preventing any OrderEntity to be persisted into the database. It just won't do anything, and no further errors are reported, which drives me nuts...
Any ideas? Thanks in advance!
Just a quick guess is that you are adding the wrong OrderEntity instance. If you pass an entity instance to em.merge() the EntityManager creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. You have to use the new copy in any further actions regarding this entity.
In code:
public void create(OrderEntity e) {
WaiterEntity waiter = em.find(WaiterEntity.class, e.getWaiter().getId());
e = em.merge(e);
if (waiter != null) waiter.getOrders().add(e);
}
I need 3 entities: User, Contract (which are a many to many relation) and a middle entity: UserContract (this is needed to store some fields).
What I want to know is the correct way to define the relationships between these entities in JPA/EJB 3.0 so that the operations (persist, delete, etc) are OK.
For example, I want to create a User and its contracts and persist them in a easy way.
Currently what I have is this:
In User.java:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<UserContract> userContract;
In Contract.java:
#OneToMany(mappedBy = "contract", fetch = FetchType.LAZY)
private Collection<UserContract> userContract;
And my UserContract.java:
#Entity
public class UserContract {
#EmbeddedId
private UserContractPK userContractPK;
#ManyToOne(optional = false)
private User user;
#ManyToOne(optional = false)
private Contract contract;
And my UserContractPK:
#Embeddable
public class UserContractPK implements Serializable {
#Column(nullable = false)
private long idContract;
#Column(nullable = false)
private String email;
Is this the best way to achieve my goals?
Everything looks right. My advice is to use #MappedSuperclass on top of #EmbeddedId:
#MappedSuperclass
public abstract class ModelBaseRelationship implements Serializable {
#Embeddable
public static class Id implements Serializable {
public Long entityId1;
public Long entityId2;
#Column(name = "ENTITY1_ID")
public Long getEntityId1() {
return entityId1;
}
#Column(name = "ENTITY2_ID")
public Long getEntityId2() {
return entityId2;
}
public Id() {
}
public Id(Long entityId1, Long entityId2) {
this.entityId1 = entityId1;
this.entityId2 = entityId2;
}
}
protected Id id = new Id();
#EmbeddedId
public Id getId() {
return id;
}
protected void setId(Id theId) {
id = theId;
}
}
I omitted obvious constructors/setters for readability. Then you can define UserContract as
#Entity
#AttributeOverrides( {
#AttributeOverride(name = "entityId1", column = #Column(name = "user_id")),
#AttributeOverride(name = "entityId2", column = #Column(name = "contract_id"))
})
public class UserContract extends ModelBaseRelationship {
That way you can share primary key implementation for other many-to-many join entities like UserContract.