how can we update all references of an entity with hibernate? - java

Is there any way to update all of the references of an entity loaded into our program by hibernate all at time?
for example, I have loaded the product with id "1" in class A and change a new load of the same product with id "1" in class B. but the one in class A didn't change.
how should I fix this?
This is part of the code :
Product class :
#Entity
#Table(name = "t_product")
public class Product {
#Setter(AccessLevel.NONE)
#Id #GeneratedValue #Column(name = "ID")
private int id;
...
#ElementCollection
#OneToMany(targetEntity = SellerIntegerMap.class,
fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
#JoinTable(name = "t_product_stock")
private List<SellerIntegerMap> stock;
...
}
SellerIntegerMap :
#Entity
#Table(name = "t_map")
public class SellerIntegerMap{
#Setter(AccessLevel.NONE)
#Id #GeneratedValue
private int id;
#ManyToOne
#JoinColumn(name = "SELLER")
private Seller seller;
#Column(name = "INTEGER_VALUE")
private Integer integer;
}
DBManager :
public class DBManager {
public static <T> T load(Class<T> type, Serializable serializable){
Session session = HibernateUtil.getSession();
session.beginTransaction();
T object = session.get(type,serializable);
if (object == null){
}
session.evict(object);
session.getTransaction().commit();
return object;
}
public static void save(Object object){
Session session = HibernateUtil.getSession();
session.beginTransaction();
session.saveOrUpdate(object);
session.getTransaction().commit();
}
}
and the test :
public void test(){
Product product = DBManager.load(Product.class,1);
Product productDup = DBManager.load(Product.class,1);
List<SellerIntegerMap> list = productDup.getStock();
list.get(0).setInteger(25);
DBManager.save(productDup);
}
The data is updated in SQL table but not in "product" which is a same entity like "productDup". how can I solve the problem ? is there any way to solve it in the program not loading the data every time we need them?

You need one transaction over the whole operation. That way, Hibernate will keep only a single managed instance of that entity.

Related

Is there a way to retrieve an entity with list property but load the list with the last few entities?

Let's say I have the following entities in my application:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
#Data
#Entity
public class AnotherEntity {
#Id
private Long id;
#Column
private String someField;
#Column
private Long version;
}
Question 1:
I want to load a SomeEntity with id = 1 for example, but I only want to load the anotherEntities partially, for example I only want the last 10 versions of it, what is the easiest and most straight forward way of doing it (with Hibernate/Spring Data JPA) with one request?
Question 2:
I want to update the previously mentioned object and add a new AnotherEntity to the list, but the save(T t) method of JpaRepository saves the whole object and I lose the ones that weren't loaded. How can I save the object so that the version will be updated by Spring Data (Optimistic Locking) and the SomeEntity won't lose previous data?
Update 1:
I am using Postgresql for database.
You have different options depending on your exact constraints.
You can use the #Where annotation:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
#Where(clause = "version < 10")
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
You can use a filter:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
#Filter(
name="latestVersions",
condition="version < :version"
)
private List<AnotherEntity> anotherEntities = new ArrayList<>();
}
You can enable filters before running a query with the session:
entityManager
.unwrap(Session.class)
.enableFilter("latestVersions")
.setParameter("version", 10);
List<Account> accounts = entityManager.createQuery(
"from SomeEntity se where se.id = 1", SomeEntity.class)
.getResultList();
You can map the association as bidirectional (or as a many-to-one)
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany(mappedBy = "someEntity")
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
#Data
#Entity
public class AnotherEntity {
#Id
private Long id;
#Column
private String someField;
#Column
private Long version;
#ManyToOne
private SomeEntity someEntity;
}
Now you can get the list of entities using an HQL query:
from AnotherEntity ae where ae.someEntity.id = 1 and ae.version < 10
When you want to create a new AnotherEntity, you can get SomeEntity from any element in the result list, or you can use EntityManager#getReference (and avoid running the query):
AnotherEntity ae = new AnotherEntity(...);
ae.setSomeEntity(em.getReference(SomeEntity.class, 1));
em.persist(ae);
The association is lazy, so Hibernate is not going to load the whole collection (unless you need to access it).

Hibernate insert null values ​in composite foreign key

I have two simple domain objects as follows:
USER:
#Entity
#Table(name="USER")
#IdClass(UserPK.class)
public class User implements Serializable {
//...
#Id
#Column(name = "FISCALCODE")
private String fiscalCode;
#Id
#Column(name = "USERNUMBER")
private String userNumber;
#OneToMany(mappedBy="user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Items> items;
// getters and setters
}
UserPK:
public class UserPK implements Serializable {
#Column(name = "FISCALCODE")
private String fiscalCode;
#Column(name = "USERNUMBER")
private String userNumber;
// getter and setter
}
ITEMS:
#Entity
#Table(name="ITEMS")
public class Items implements Serializable {
//...
#Id
#Column(name = "ID_ITEM")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_item_generator")
#SequenceGenerator( name = "id_item_generator", sequenceName = "ITEM_SEQ", allocationSize = 1)
private Integer id;
#ManyToOne
#JoinColumns({
#JoinColumn(name="FISCALCODE"),
#JoinColumn(name="USERNUMBER")
})
private User user;
// getters and setters
}
DB Table:
user { fiscalcode, usernumber, other columns... } // fiscalcode+usernumber = PK
items { id, fiscalcode, usernumber, other columns... } // fiscalcode,usernumber is a foreign key
CONTROLLER:
#RequestMapping(value="/user", method = RequestMethod.POST, produces = "application/json")
public Object postUser(#RequestBody(required = false) User user){
//connection etc..
session.save(user);
//...
return new ResponseEntity<>(HttpStatus.OK);
}
Why when I run the command session.save(user) Hibernate insert value null in the columns FISCALCODE and USERNUMBER of the ITEMS table?
I tried to set the ManyToOne and the JoinColumns on the getter, but the result is the same.
EDIT: i have added my method for POST operation
As it's stated in the documentation for #JoinColumns:
When the JoinColumns annotation is used, both the name and the referencedColumnName elements must be specified in each such JoinColumn annotation.
So, you should correct your mapping like below:
#ManyToOne
#JoinColumns({
#JoinColumn(name="FISCALCODE", referencedColumnName = "FISCALCODE"),
#JoinColumn(name="USERNUMBER", referencedColumnName = "USERNUMBER")
})
private User user;
Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times.
So, you should have in your User entity the addItem() and removeItem() utility methods that synchronize both ends whenever a child element is added or removed like below.
#Entity
#Table(name="USER")
#IdClass(UserPK.class)
public class User implements Serializable {
//...
#OneToMany(mappedBy="user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Items> items;
// getters and setters
public void addItem(Items item) {
this.items.add(item);
item.setUser(this);
}
public void removeItem(Items item) {
this.items.remove(item);
item.setUser(null);
}
}
Example of saving:
User user = new User();
user.setFiscalCode("Code1");
user.setUserNumber("User1");
// ...
Items item1 = new Items();
Items item2 = new Items();
// ...
user.addItem(item1);
user.addItem(item2);
session.save(user);

JPA 1:N relationship removing child does not remove it from parent

I have the following objects:
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Entity(name="Group")
public class Group {
#Id
#GeneratedValue
#NotNull
#Column(name = "GROUP_ID")
private Long id;
#Column(name="NAME")
private String name;
#OneToMany(
targetEntity = Product.class,
mappedBy = "groupId",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private List<Product> products = new ArrayList<>();
public Group(String name) {
this.name = name;
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity(name="Product")
public class Product {
#Id
#GeneratedValue
#NotNull
#Column(name="PRODUCT_ID")
private Long id;
#Column(name="NAME")
private String name;
#Column(name="DESCRIPTION")
private String description;
#Column(name="PRICE")
private double price;
#ManyToMany
#JoinTable(
name = "JOIN_PRODUCT_CART",
joinColumns = {#JoinColumn(name = "PRODUCT_ID", referencedColumnName = "PRODUCT_ID")},
inverseJoinColumns = {#JoinColumn(name = "CART_ID", referencedColumnName = "CART_ID")}
)
private List<CartEntity> carts = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "GROUP_ID")
private Group groupId;
public Product(String name, String description, double price) {
this.name = name;
this.description = description;
this.price = price;
}
public Product(String name, String description, double price, Group groupId) {
this(name, description, price);
this.groupId = groupId;
}
public void addToCart(CartEntity cart) {
this.carts.add(cart);
cart.getProductsList().add(this);
}
public void addGroup(Group group) {
group.getProducts().add(this);
this.groupId = group;
}
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "cart")
public class CartEntity {
#Id
#NotNull
#GeneratedValue
#Column(name = "CART_ID")
private Long id;
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "carts")
private List<Product> productsList = new ArrayList<>();
public void addProduct(Product product) {
productsList.add(product);
product.getCarts().add(this);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CartEntity that = (CartEntity) o;
return id.equals(that.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
Now, when I have the following test:
public class ProductDaoTestSuite {
#Autowired
private ProductDao productDao;
#Autowired
private CartDaoStub cartDaoStub;
#Autowired
private GroupDao groupDao;
#Test
public void testDeleteProduct() {
// Given
Product product = new Product("test", "testProduct", 100.0);
Group group = new Group("group1");
CartEntity cart = new CartEntity();
product.addGroup(group);
cart.addProduct(product);
// When
groupDao.save(group);
productDao.save(product);
cartDaoStub.save(cart);
Long groupId = group.getId();
Long productId = product.getId();
Long cartId = cart.getId();
productDao.deleteById(productId);
// Then
Assert.assertTrue(cartDaoStub.findById(cartId).isPresent());
Assert.assertEquals(0, cartDaoStub.findById(cartId).get().getProductsList().size());
Assert.assertTrue(groupDao.findById(groupId).isPresent());
Assert.assertEquals(0, groupDao.findById(groupId).get().getProducts().size());
Following product deletion, I would expect association with it in group and cart to disappear (product to disappear from their List relationship fields). However, that is not happening at the moment. When I use Group/Cart Dao to pull group & cart from the DB after product deletion, they still have product in their Lists, while product when pulled from the DB is returned as null.
I have tried to add "orphanRemoval = true" value for #OneToMany adnotation, but it did not seem to work for Group entity.
What am I doing wrong?
I have started experimenting with adding all types of cascade (except for REMOVE) to #ManyToOne on Product class, but so far no luck.
For 1:N, yours should work just fine with minor adjustment.
The reason why it fails: Upon doing "groupDao.save(group);" this group is now in the persistence context and calling "groupDao.findById(groupId).get().getProducts().size()" would return the copy which is from the persistence context.
To solve this: simply add: entityManager.flush(); and entityManager.clear(); before the Assert
I would like to demonstrate it with this Integration Test
#Test
#Transactional
public void deleteProduct_groupShouldNowBeEmpty() {
ProductGroup group = groupRepository.findById("0001").orElseThrow(() -> new IllegalArgumentException("id not found"));
Assert.assertEquals(1, group.getProducts().size());
Product product = productRepository.findById("0001").orElseThrow(() -> new IllegalArgumentException("id not found"));
productRepository.delete(product);
entityManager.flush();
entityManager.clear();
Assert.assertEquals(0, productRepository.findAll().size());
Assert.assertEquals(0, groupRepository.findById("0001").get().getProducts().size());
}
If we are to remove the first 2 lines, then we won't need to flush and clear. Like this.
#Test
#Transactional
public void deleteProduct_groupShouldNowBeEmpty() {
Product product = productRepository.findById("0001").orElseThrow(() -> new IllegalArgumentException("id not found"));
productRepository.delete(product);
Assert.assertEquals(0, productRepository.findAll().size());
Assert.assertEquals(0, groupRepository.findById("0001").get().getProducts().size());
}
For N:M, since there would be another table where product is being referenced, then we would need to delete the records from that table first before deleting the product.
N:M is a bit tricky so if I can suggest domain changes, here how I'll do it. (The integration test is at the bottom.)
I'll add a separate entity: CartItem
which is associated to a Product and Cart
#Entity
public class CartItem {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
#ManyToOne
private Product product;
#ManyToOne
private Cart cart;
public String getId() {
return id;
}
// Required by JPA
protected CartItem() {}
}
And for the Product Entity: add a bidirectional relationship with CartItem
#Entity
public class Product {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
private String name;
private String description;
private BigDecimal price;
#ManyToOne
private ProductGroup group;
#OneToMany(mappedBy = "product")
private List<CartItem> cartItems;
public List<CartItem> getCartItems() {
return cartItems;
}
// Required by JPA
protected Product() {}
}
Then, retrieve the product (using Join Fetch to avoid N+1, since later will be looping through each cartItem)
public interface ProductRepository extends JpaRepository<Product, String> {
#Query("SELECT product FROM Product product JOIN FETCH product.cartItems")
Optional<Product> findProduct(String Id);
}
create another query inside CartItemRepository to delete cartItems in bulk by ids
public interface CartItemRepository extends JpaRepository<CartItem, String> {
#Modifying
#Query("DELETE FROM CartItem cartItem WHERE cartItem.id IN :ids")
void deleteByIds(#Param("ids") List<String> ids);
}
Lastly here's the integration test to wrap everthing up:
#Test
#Transactional
public void deleteProduct_associatedWithCart() {
Cart cart = cartRepository.findById("0001").get();
Assert.assertEquals(1, cart.getCartItems().size());
Product product = productRepository.findProduct("0001").orElseThrow(() -> new IllegalArgumentException("id not found"));
List<String> cartItemIds = product.getCartItems().stream()
.map(CartItem::getId)
.collect(Collectors.toList());
cartItemRepository.deleteByIds(cartItemIds);
productRepository.delete(product);
entityManager.flush();
entityManager.clear();
Assert.assertEquals(0, productRepository.findAll().size());
Assert.assertEquals(0, groupRepository.findById("0001").get().getProducts().size());
Assert.assertEquals(0, cartItemRepository.findAll().size());
Assert.assertEquals(0, cartRepository.findById("0001").get().getCartItems().size());
}
I've used DBUnit for this integration test so I think it would also be helpful to share the dataset.
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<product_group id="0001" name="product group with 1 product"/>
<product id="0001" group_id="0001" />
<cart id="0001" />
<cart_item id="0001" product_id="0001" cart_id="0001" />
</dataset>
When you remove an entity, this state transition should be propagated from parent to child, not the other way around.
In this case, you need to move that functionally to the Group entity, something like this:
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Entity(name="Group")
public class Group {
#Id
#GeneratedValue
#NotNull
#Column(name = "GROUP_ID")
private Long id;
#Column(name="NAME")
private String name;
#OneToMany(
targetEntity = Product.class,
mappedBy = "groupId",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY, // Always prefer LAZY initialized Collections to EAGER ones
orphanRemoval = true
)
private List<Product> products = new ArrayList<>();
public Group(String name) {
this.name = name;
}
public void addProduct(Product product){
product.setGroupId(this);
this.products.add(product);
}
public void removeProduct(Product product){
product.setGroupId(null);
this.products.remove(product);
}
If you want to remove a Product, you only need to invoke the removeProduct method and save the parent entity:
Group group = new Group("group1");
Product product = new Product("test", "testProduct", 100.0);
group.addProduct(product);
groupDao.save(group);
On the other hand, we have the many-to-many relation between Product and CartEntity.
First, if you configure the entity CartEntity with Cascade.ALL as in your example:
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "carts")
private List<Product> productsList = new ArrayList<>();
It will have a probably undesired effect: if you remove the CartEntity, it will remove all the Products associated with the entity as well, even if other CartEntitys are still associated to them. Vlad Mihalcea explain it in great detail in this article.
To avoid that problem, the best option will be just define the relationship as follows:
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "carts")
private List<Product> productsList = new ArrayList<>();
This will give us a CartEntity like this:
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "cart")
public class CartEntity {
#Id
#NotNull
#GeneratedValue
#Column(name = "CART_ID")
private Long id;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "carts")
private List<Product> productsList = new ArrayList<>();
public void addProduct(Product product) {
productsList.add(product);
product.getCarts().add(this);
}
public void removeProduct(Product product) {
productsList.remove(product);
product.getCarts().remove(this);
}
public void removeProducts() {
for(Product product : new ArrayList<>(products)) {
removeProduct(product);
}
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CartEntity that = (CartEntity) o;
return id.equals(that.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
Please, note the inclusion of the removeProduct and removeProducts methods.
With this code, if you need to remove a CartEntity, just do the following:
cart.removeProducts();
cartDao.remove(cart);
And if you need to remove a Product from the CartEntity (will only remove the relation):
cart.removeProduct(product);
cartDao.save(cart);
If you need to propagate the Product remove to the CartEntity, I think that the best option will be create a business method that takes care of the whole process. Think in something like:
public void removeProduct(Product product){
Group group = product.getGroupId();
group.removeProduct(product);
final List<CartEntity> carts = product.getCarts();
if (carts != null) {
for(CartEntity cart : new ArrayList<>(carts)) {
cart.removeProduct(product);
cartDao.save(cart);
}
}
groupDao.save(group);
}
It will remove the association, you just need to do small adjustments.
1:N. When you remove Product, you don't have to do anything else in order to remove its association with Group, because the product itself holds the association (in DB column product.group_id). You just need to commit the transaction. And next time when you load a group from the DB it for sure will not contain this product.
N:M. There is no way to automatically remove the association because it is stored in a separate table and you don't have a separate entity for it. (YOU SHOULD NOT USE CascadeType.ALL for N:M relations). What you want to do is remove the association before you remove the product. Just add another helper method to Product.
public void removeFromCarts() {
carts.forEach(c -> c.getProducts().remove(this));
carts.clear();
}
So finally, in order to remove a product and all the associations with it. You will need to do the following:
product.removeFromCarts();
productDao.deleteById(productId); // not sure why you remove by id (not pass object)
*please note that you need to commit transaction and close the session. So you cannot rely on the test. In real app when you do what I described, it will work
**N:M is tricky. For instance, you should better use Set instead of List to avoid unexpected SQL under the hood. Also going down the road, I recommend you to consider splitting N:M into two N:1 and 1:M and have a dedicated Entity for a link table
Not sure I follow. Hibernate does not automatically maintain the inverse association for you. You can make it sensitive to changes on the owning side of the association, but that's as far as it goes.
As to why your test fails, cartDaoStub.findById(cartId) probably returns the same copy of the CartEntity that you already have loaded into the persistence context. Try calling entityManager.flush() followed by entityManager.clear() before making the assertion and the issue will probably go away.

criteria for #OneToMany association

A Student class has OneToOne association with an Object of type Resume and Resume class has a OneToMany association with a collection of type Master and master has a property degreeName.
What i want to achieve is:
select all the students where student.resume.masters.degreeName in (?,?,?,.....)
It should search in all the collection(Master) objects.
Below code has no compilation error but It is not giving expected result please correct me.
Student Entity:
#Entity
public class Student {
#OneToOne(fetch = FetchType.LAZY, mappedBy = "student", cascade = CascadeType.ALL)
private Resume resume;
}
Resume Entity:
#Entity
public class Resume {
#OneToMany(mappedBy="resume",cascade=CascadeType.ALL)
private List<Master> masters=new ArrayList<>();
#OneToOne(fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
private Student student;
}
Master Entity:
#Entity
public class Master {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="resume_id")
private Resume resume;
private String degreeName;
}
Dao:
Criteria studentCriteria = session.createCriteria(Student.class);
Criteria resumeCriteria = studentCriteria.createCriteria("resume");
Criteria mastersCriteria = resumeCriteria.createCriteria("masters");
List<String> degreeslist = new ArrayList<>(Arrays.asList(degrees));
//degreeList is collection of values on which the student will be searched
if (degreeslist.size() == 1) {
mastersCriteria.add(Restrictions.eq("degreeName",
degreeslist.get(0)));
} else {
mastersCriteria.add(Restrictions.in("degreeName", degreeslist));
}
Criteria mastersCriteria = getSession().createCriteria(Student.class, "student");
mastersCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
mastersCriteria.createAlias("student.resume", "resume");
mastersCriteria.createAlias("resume.masters","masters");
if (degreeslist.size() == 1) {
mastersCriteria.add(Restrictions.eq("masters.degreeName", degreeslist.get(0)));
} else {
mastersCriteria.add(Restrictions.in("masters.degreeName", degreeslist));
}
List<Student> students = mastersCriteria.list();

JPA not saving foreign key to #OneToMany relation

I'm using Spring with Hibernate as a JPA provider and are trying to get a #OneToMany (a contact having many phonenumbers) to save the foreign key in the phone numbers table. From my form i get a Contact object that have a list of Phone(numbers) in it. The Contact get persisted properly (Hibernate fetches an PK from the specified sequence). The list of Phone(numbers) also gets persisted with a correct PK, but there's no FK to the Contacts table.
public class Contact implements Serializable {
#OneToMany(mappedBy = "contactId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Phone> phoneList;
}
public class Phone implements Serializable {
#JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
#ManyToOne
private Contact contactId;
}
#Repository("contactDao")
#Transactional(readOnly = true)
public class ContactDaoImpl implements ContactDao {
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void save(Contact c) {
em.persist(c);
em.flush();
}
}
#Controller
public class ContactController {
#RequestMapping(value = "/contact/new", method = RequestMethod.POST)
public ModelAndView newContact(Contact c) {
ModelAndView mv = new ModelAndView("contactForm");
contactDao.save(c);
mv.addObject("contact", c);
return mv;
}
}
Hopefully I got all of the relevant bits above, otherwise please let me know.
You have to manage the Java relationships yourself. For this kind of thing you need something like:
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "contact")
private List<Phone> phoneNumbers;
public void addPhone(PhoneNumber phone) {
if (phone != null) {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
...
}
#Entity
public class Phone {
#Id
private Long id;
#ManyToOne
private Contact contact;
...
}
In reply to Cletus' answer. I would say that it's important to have the #column annotation on the id fields, as well as all the sequence stuff. An alternative to using the mappedBy parameter of the #OneToMany annotation is to use the #JoinColumn annotation.
As a kinda aside your implementation of addPhone needs looking at. It should probably be something like.
public void addPhone(PhoneNumber phone) {
if (phone == null) {
return;
} else {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
If the Contact-Phone relationship is unidirectional, you can also replace mappedBy in #OneToMany annotation with #JoinColumn(name = "contact_id").
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "contact_id")
private List<Phone> phoneNumbers;
// normal getter/setter
...
}
#Entity
public class PhoneNumber {
#Id
private Long id;
...
}
Similar in JPA #OneToMany -> Parent - Child Reference (Foreign Key)
I don't think the addPhone method is necessary, you only have to set the contact in the phone object:
phone.setContact(contact);
If you want your relationship unidirectional i.e. can navigate from Contact to Phone's only, you need to add
#JoinColumn(name = "contact_id", nullable = false)
Under your #OneToMany on your parent entity.
nullable = false IS VITAL if you want hibernate to populate the fk on the child table
Try this sample:
#Entity
public class Contact {
#Id
private Long id;
#JoinColumn(name = "contactId")
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Phone> phones;
}
#Entity
public class Phone {
#Id
private Long id;
private Long contactId;
}
In JPA this helped me
contact.getPhoneList().forEach(pl -> pl.setContact(contact));
contactRepository.save(contact);

Categories