Spring Data : issue with OneToMany, child not saved properly - java

I am having trouble dealing with #OneToMany relationship.
Here is my code :
#Entity
#Table(name = "type_mouvement")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class TypeMouvement implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy="typeMouvement", fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
private List<CompteTypeMouvement> comptes;
...
}
#Entity
#Table(name = "type_mouvement_comptes")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class CompteTypeMouvement implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String numCompte;
#ManyToOne
private TypeMouvement typeMouvement;
...
}
How I use these entities :
TypeMouvement typeMouvementFromDB = typeMouvementRepository.findOne(new Long(1));
CompteTypeMouvement compte = new CompteTypeMouvement();
compte.setNumCompte("123");
compte.setTypeMouvement(typeMouvementFromDB);
typeMouvementFromDB.getComptes().add(compte);
typeMouvementRepository.save(typeMouvementFromDB);
The result I get :
I thought I would get :
Why are the properties of CompteTypeMouvement not filled when I save TypeMouvement?

You are trying to invoke save passing an already existing entity:
TypeMouvement typeMouvementFromDB = typeMouvementRepository.findOne(new Long(1));
typeMouvementRepository.save(typeMouvementFromDB);
The relationship has a persist cascade type only though:
#OneToMany(mappedBy="typeMouvement", fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
private List<CompteTypeMouvement> comptes;
The save impl is a follows (spring-data-jpa-1.11.3):
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Which means that a merge instead of a persist will be invoked.
This if you add merge to cascade it should work:
#OneToMany(mappedBy="typeMouvement", fetch = FetchType.EAGER,
cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<CompteTypeMouvement> comptes;

Related

How to map bidirectional #OneToMany and #OneToOne on the same entity

I have two enteties and I want to be able access one of them from another and vise versa (bidirectional). But sometimes when persisting an order as stopLossOrder it's not saved to position. If you have any ideas of how it can be implemented (if it's possible this way or similar) I would be glad to hear.
Later on I want to add more orders to position entity similarly to 'stopLossOrder'
#Entity(name = "Position")
#Table(name = "positions")
#Getter #Setter
public class PositionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true)
#JoinColumn(name = "stop_loss_order_id", referencedColumnName = "id")
private OrderEntity stopLossOrder;
#OneToMany(mappedBy = "position",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<OrderEntity> orders = new HashSet<>();
public void setStopLossOrder(OrderEntity stopLossOrder) {
this.stopLossOrder = stopLossOrder;
stopLossOrder.setPosition(this);
}
public boolean addOrder(OrderEntity orderEntity) {
orderEntity.setPosition(this);
return orders.add(orderEntity);
}
}
#Entity(name = "Order")
#Table(name = "orders")
#Getter
#Setter
public class OrderEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = -1462587657644552577L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(optional = false)
private PositionEntity position;
}
Persisting:
if (orderEntity.getType() == OrderType.STOP_MARKET) {
positionEntity.setStopLossOrder(orderEntity);
} else {
boolean isAdded = positionEntity.addOrder(orderEntity);
if (!isAdded)
throw new TradeServiceException("Order with id: " + order.getOrderId() + " already added to position.");
}
orderEntity = orderRepository.save(orderEntity);

Hibernate OneToMany returning two objects when there is only one in the database

I'm working on a project with Hibernate and I'm having a pretty weird behaviour. In my database there are two entities Worksheet and WorksheetField. Saved in the database I have one worksheet and one field linked to it. When I get this entry from the database it comes with 2 fields.
My Entities:
#Entity
#Table(name = "worksheet")
public class Worksheet implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, updatable = false)
private Long id;
// .......
#OneToMany(targetEntity = WorksheetField.class,
mappedBy = "worksheet",
cascade = CascadeType.ALL
)
private List<WorksheetField> fields;
// .......
}
#Entity
#Table(name = "worksheet_field")
public class WorksheetField implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
// .......
#ManyToOne
#JoinColumn(name="worksheet_id",nullable = false)
private Worksheet worksheet;
// .......
}
Repository:
public interface WorksheetRepository extends JpaRepository<Worksheet, Long>, JpaSpecificationExecutor<Worksheet> {
List<Worksheet> findAllByWorksheetType(Worksheet.Type type);
#Override
#Modifying
#Transactional
Worksheet save(Worksheet worksheet);
#Override
#Transactional
Worksheet findOne(Long id);
}
The #Transactional on findOne is just an attempt at fixing this, don't worry about that. And I fetch this entry by using findOne.
Looking at these, is it possible to identify what I'm doing wrong?

Hibernate - the custom query did not find the entity by the child parameter for ManyToOne unidirectional relation

I have a problem with retrieving an entity using the child's entity as a search parameter. Entities are related to many to one relationship as unidirectional and each object is fetched as FetchType.LAZY.
When I looking for an entity by a child entity, the result is null. But when I set to fetch as Eager it is correct.
My Entities:
#NoArgsConstructor
#Getter
#Entity
#Table(name = "partner")
public class PartnerEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String login;
public PartnerEntity(String login) {
this.login = login;
}
}
#NoArgsConstructor
#Getter
#Entity
#Table(name = "point")
public class PointEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "partner_Id")
private PartnerEntity partnerEntity;
public PointEntity(PartnerEntity partnerEntity) {
this.partnerEntity = partnerEntity;
}
}
#NoArgsConstructor
#Getter
#Entity
#Table(name = "orer")
public class OrdEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PAYMENT_POINT_ID")
private PointEntity pointEntity;
public OrdEntity(PointEntity pointEntity) {
this.pointEntity = pointEntity;
}
}
#NoArgsConstructor
#ToString
#Getter
#Entity
#Table(name = "BL")
public class BLEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARTNER_LOGIN", referencedColumnName = "login")
private PartnerEntity partnerEntity;
private String number;
public BLEntity(PartnerEntity partnerEntity, String number) {
this.partnerEntity = partnerEntity;
this.number = number;
}
}
And I looking for BLEntity using OrdEntity child:
final OrdEntity byId = ordRepo.findById(id);
final PartnerEntity partnerEntity = order.getPointEntity().getPartnerEntity();
final BLEntity blEntityResult= blRepo.findOneByNumberAndPartner(number, partnerEntity);
The object partnerEntity is not null, it is correct object.
I got blEntityResult as null but if I change in PointEntity fetch to FetchType.EAGER, blEntityResult is not null(correct).
My custom query in repository below:
public interface BLRepo extends JpaRepository<BLEntity, Long> {
#Query("select b from BLEntity b where b.number = :number and b.partnerEntity= :partner")
BLEntity findOneByNumberAndPartner(#Param("number") String number, #Param("partner") PartnerEntity partner);
}
why does happens, if the partner object being downloaded is not null and is correct?
I think you should add the mapping in both sides,
because of default fetch type for #AllToMany=Lazy and #ManyToAll = Eager.
just add below code inside PartnerEntity.
#OneToMany(mappedBy="partnerEntity" , fetch = FetchType.Eager )
List<BLEntity> blEntity = new ArrayList<>();
I change FetchType into Eager in PointEntity:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "partner_Id")
private PartnerEntity partnerEntity;
And everything is ok, but I don't understand why it does not work with PaymentType.Lazy. When I am looking for:
final PartnerEntity partnerEntity = order.getPointEntity().getPartnerEntity();
I get correct entity "PartnerEntity" which has proper login's field (login'field has value "test").
When I turned logged level to 'TRACE' I saw, Hibernate not binding correct login's parameter, it set null instead "test") why? :)

cascadeType.REMOVE not working(JPA spring data)

Below is my code. I want to delete all the records of tracking_event when i delete tracking table record which are in oneToMany relationship
Tracking entity
#Entity
#Table(name = "TRACKING")
public class Tracking {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TRACKING_ID")
private int id;
#JsonIgnore
#OneToMany(mappedBy = "tracking",cascade = CascadeType.REMOVE)
private List<TrackingEvent> trackingEvents;
}
TrackingEvent entity
#Entity
#Table(name = "TRACKING_EVENT")
public class TrackingEvent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TRACKING_EVENT_ID")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TRACKING_ID")
private Tracking tracking;
}
TrackingRepository interface
public interface TrackingRepository extends CrudRepository<Tracking, Integer> {
#Modifying
#Transactional
#Query(" DELETE FROM Tracking WHERE id = :tid")
void deleteByUpdatedDate(#Param("tid") int tid);
}
I also tried using
#OneToMany(mappedBy = "tracking",cascade = CascadeType.REMOVE,orphanRemoval = true)
private List<TrackingEvent> trackingEvents;`

Persistense #OneToMany - #ManyToOne

I'm having trouble persists the following entities:
#Entity
#Table(name="entityOne")
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne")
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
In EntityOneDAO:
em.merge(entityOne);
And it is only persisted to EntityOne and not the list of EntityTwo ... How do I persist the list ?
Thanks all
You need to take care of both:
transitive persistence (using Cascade)
synchronizing both end of the bi-directional association.
So EntityOne should Cascade Persist and Merge to EntityTwo:
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne", cascade = { CascadeType.PERSIST, CascadeType.MERGE})
private List<EntityTwo> entities = new ArrayList<>();
As you can see, you should always initialize your collection classes to avoid unnecessary null checks.
And it's always better to add the following helper child adding utility in your parent classes (e.g. EntityOne)
public void addChild(EntityTwo child) {
if(child != null) {
entities.add(child);
child.setEntityOne(this);
}
}
Then you can simply call:
EntityOne entityOne = new EntityOne();
entityOne.setProperty("Some Value");
EntityTwo entityTwo_1 = new EntityTwo();
entityTwo_1.setName("Something");
EntityTwo entityTwo_2 = new EntityTwo();
entityTwo_2.setName("Something");
entityOne.addChild(entityTwo_1);
entityOne.addChild(entityTwo_2);
entityManager.persist(entityOne);
P.S.
Please remove the #Inject annotation from the EntityTwo class. Entities are not Components.
And persist is much more efficient than merge, when you want to insert new entities.
You should explicitly set each entityTwo objects' entityOne field.
Such that:
entityTwo_1.setEntityOne(entityOne);
entityTwo_2.setEntityOne(entityOne);
entityOne.entities.add(entityTwo_1);
entityOne.entities.add(entityTwo_2);
em.merge(entityOne);
Try this:
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne",
cascade = { CascadeType.ALL})
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
You can read here, about the CascadeType.
edited.

Categories

Resources