One-To-Many - How to Link to Objects correctly? - java

I'm using JPA and have some difficulties to understand how the One-To-Many Realtionship works.
I have the following two classes:
#Entity
public class myCheck {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
protected int Check_id;
#Column
private String name;
#ManyToOne
private mySystem system;
#Override
public String toString() {
return this.name;
}
public int getId() {
return Check_id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public mySystem getLinkSystem() {
return system;
}
public void linkSystem(mySystem system) {
this.system = system;
}
}
and:
#Entity
public class mySystem {
#Id
#GeneratedValue
#Column(name = "system_id")
protected int system_id;
#Column
public String name;
#ManyToOne(cascade=CascadeType.ALL)
private mySystem parent;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "system", fetch = FetchType.EAGER)
private List<myCheck> checks;
public mySystem() {
//subSystems = new ArrayList<mySystem>();
checks = new ArrayList<myCheck>();
}
public boolean linkCheck(myCheck hc) {
return checks.add(hc);
}
public boolean unlinkCheck(myCheck hc) {
return checks.remove(hc);
}
public List<myCheck> getlinkedChecks() {
return checks;
}
public myCheck getLinkCheck(int hcId) {
for (myCheck hc : checks) {
if (hc.getId() == hcId)
return hc;
}
return null;
}
public int getId() {
return system_id;
}
public void setId(int id) {
this.system_id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return this.getName();
}
}
Now I have an existing System in my database, which is loaded:
// load System
EntityManager entityManager1 = entityManagerFactory.createEntityManager();
List<mySystem> systems = entityManager1.createQuery("from mySystem").getResultList();
entityManager1.close();
I want to add two new checks to Systems. What is working is:
EntityManager entityManager2 = entityManagerFactory.createEntityManager();
entityManager2.getTransaction().begin();
myCheck check = new myCheck();
check.setName("Check 1");
check.linkSystem(systems.get(0));
entityManager2.persist(check);
myCheck check2 = new myCheck();
check2.setName("Check 2");
check2.linkSystem(systems.get(0));
entityManager2.persist(check2);
entityManager2.merge(systems.get(0));
entityManager2.getTransaction().commit();
entityManager2.close();
But I can't do this:
EntityManager entityManager2 = entityManagerFactory.createEntityManager();
entityManager2.getTransaction().begin();
myCheck check = new myCheck();
check.setName("Check 1");
systems.get(0).linkCheck(check);
entityManager2.persist(check);
myCheck check2 = new myCheck();
check2.setName("Check 2");
systems.get(0).linkCheck(check);
entityManager2.persist(check2);
entityManager2.merge(systems.get(0));
entityManager2.getTransaction().commit();
entityManager2.close();
The second solution will save the checks, but I don't link them with the system.
Has someone a explanation for this? I really want to understand this.

You have bidirectional relation which means each side of the relation should have a reference to the other side.
so in your persistence logic you will also need to inject the system in your check
myCheck check = new myCheck();
check.setName("Check 1");
check.linkSystem(systems.get(0);
systems.get(0).linkCheck(check);
entityManager2.persist(check);
But in this case you will have a problem if your (systems.get(0)) is not attached to the persistence context because you will be having reference to deatached object when persisting the check, you can either put Cascade on the system inside check class or instead persist the system, it already cascades the check so the check will be persisted

Related

Are entities relationship correct?

In my project I try yo use Spring data Jpa. My find methods(findById, findAll) works correctly, but delete and save method works with problems. Delete method delete only from duck table. Save doesn't work:
Exception in thread "main" org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find springdata.entities.FrogJpa with id 2; nested exception is javax.persistence.EntityNotFoundException: Unable to find springdata.entities.FrogJpa with id 2
I have 2 entities: Frog and Duck. Every ducks have 1 Frog(OneToOne). There are problems with entities relationship?
There are my entities class:
#Entity
#Table(name = "DUCKS")
public class DuckJpa implements Serializable {
#Id
private int id;
#Column(name = "NAME")
private String name;
#Column(name = "FLY")
private String flyBehavior;
#Column(name = "QUACK")
private String quackBehavior;
#OneToOne(optional = false)
#JoinColumn(name = "FROG_ID", unique = true, nullable = false, updatable = false)
private FrogJpa frogJpa;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setFlyBehavior(String flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(String quackBehavior) {
this.quackBehavior = quackBehavior;
}
public void setFrogJpa(FrogJpa frogJpa) {
this.frogJpa = frogJpa;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getFlyBehavior() {
return flyBehavior;
}
public String getQuackBehavior() {
return quackBehavior;
}
public FrogJpa getFrogJpa() {
return frogJpa;
}
And Frog:
#Entity
#Table(name = "FROGS")
public class FrogJpa {
#OneToOne(optional = false, mappedBy = "frogJpa")
private DuckJpa duckJpa;
#Id
private int id;
#Column(name = "name")
private String name;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setDuckJpa(DuckJpa duckJpa) {
this.duckJpa = duckJpa;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public DuckJpa getDuckJpa() {
return duckJpa;
}
}
My service class:
public interface DuckService {
List<DuckJpa> findAll();
Optional<DuckJpa> findById(Integer i);
DuckJpa save(DuckJpa duckJpa);
void delete(DuckJpa duckJpa);
}
And it's implementation:
#Service("springJpaDuckService")
#Transactional
public class DuckServiceImpl implements DuckService {
#Autowired
private DuckJpaRepository duckJpaRepository;
#Transactional(readOnly = true)
public List<DuckJpa> findAll() {
return new ArrayList<>(duckJpaRepository.findAll());
}
#Override
public Optional<DuckJpa> findById(Integer i) {
return duckJpaRepository.findById(i);
}
#Override
public DuckJpa save(DuckJpa duckJpa) {
duckJpaRepository.save(duckJpa);
return duckJpa;
}
#Override
public void delete(DuckJpa duckJpa) {
duckJpaRepository.delete(duckJpa);
}
Use #OneToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY).
For more information please refer What is cascading in Hibernate?

How map Child Object input json in jackson?

I'm developing an application using java with hibernate 4.2.6 and spring 4.0.1. My application is a REST FULL application. for this I use jackson. My entities are as follow:
Calk.java:
#Entity
public class Calk {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="calk", fetch=FetchType.LAZY)
List<BaseLayer> baseLayer = new ArrayList<BaseLayer>();
public void addBaseLayer(BaseLayer baseLayer){
this.baseLayer.add(baseLayer);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonIgnore
public List<BaseLayer> getBaseLayer() {
return baseLayer;
}
public void setBaseLayer(List<BaseLayer> baseLayer) {
this.baseLayer = baseLayer;
}
}
BaseLayer.java:
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="layer")
#JsonSubTypes(
{
#JsonSubTypes.Type(value=PointLayer.class, name="point")
})
#DiscriminatorValue("BaseLayerDiscriminator")
public class BaseLayer {
#Id
#GeneratedValue
protected Long gid;
#ManyToOne(fetch = FetchType.LAZY)
protected Calk calk;
public Long getGid(){
return gid;
}
public void setGid(Long gid){
this.gid = gid;
}
#JsonIgnore
public Calk getCalk(){
return calk;
}
public void setCalk(Calk calk){
this.calk = calk;
}
}
Now I have a class that extends from BaseLayer.java as follow:
PointLayer.java:
#Entity
#DiscriminatorValue("PointDiscriminator")
public class PointLayer extends BaseLayer{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now I create a json as follow and then send it to a controller:
{"layer": "point", "calk":{"id":1}, "name": "itsme"}
Now the controller defines as follow:
#RequestMapping("\test")
public String test(#RequestBody BaseLayer baseLayer){
System.out.println(baseLayer.getName());// this print "itsme"
Calk calk = baseLayer.getCalk();//it return null
if(calk == null)
return "its null";
else
return "its not null";
}
when we call the controller it return its not null. The calk should not be null.
Where is the problem?
Update:
When I remove #JsonIgnore at getCalk, It work fine. But Why? I want to ignore getCalk but NOT ignore setCalk.
#JsonIgnore in follow:
#JsonIgnore
public List<BaseLayer> getBaseLayer() {
return baseLayer;
}
set Ignore to following by default:
public void setBaseLayer(List<BaseLayer> baseLayer) {
this.baseLayer = baseLayer;
}
You must add #JsonProperty("baseLayer") On top of set function as follow:
#JsonProperty("baseLayer")
public void setBaseLayer(List<BaseLayer> baseLayer) {
this.baseLayer = baseLayer;
}

DTOs and entities which implements the same interface

I have the next maven projects:
project-model : I have JPA entities
project-rest : Spring data, spring rest based on spring boot
project-client : Jersey clients to consume the rest services
project-web : Only jsf web application
project-desktop : Java Fx desktop application
project-android : Mobile application which consumes my Rest web services.
I'm planing to remove the JPA entities from the project-model and place there only DTO's pojos and interfaces and place my JPA entities in the rest project in order to remove the jpa dependencies from the project-model. This is because I don't want to have JPA dependencies in the project-android, project-web and project-desktop.
I was thinking to follow the next schema:
#JsonSerialize(as=CountryDto.class)
#JsonDeserialize(as=CountryDto.class)
public interface ICountry extends Serializable
{}
#Entity
#Table(name = "COUNTRY")
#JsonSerialize(as=Country.class)
#JsonDeserialize(as=Country.class)
public class Country implements ICountry
{}
public class CountryDto implements ICountry
{}
And if I need to convert from Entities to DTO's use mapstruct or Selma.
But I'm not sure if this is the best practice because I have problems in my code like the next:
#JsonSerialize(as=CityDto.class)
#JsonDeserialize(as=CityDto.class)
public interface ICity extends Serializable
{
public Integer getIdCity();
public void setIdCity(Integer idCity);
public String getName();
public void setName(String name);
public ICountry getCountryId();
public void setCountryId(ICountry countryId);
}
public class CityDto implements ICity
{
private static final long serialVersionUID = -6960160473351421716L;
private Integer idCity;
private String name;
private CountryDto countryId;
public CityDto()
{
// TODO Auto-generated constructor stub
}
public CityDto(Integer idCity, String name, CountryDto countryId)
{
super();
this.idCity = idCity;
this.name = name;
this.countryId = countryId;
}
public CityDto(Integer idCity, String name)
{
super();
this.idCity = idCity;
this.name = name;
}
#Override
public Integer getIdCity()
{
return idCity;
}
#Override
public void setIdCity(Integer idCity)
{
this.idCity = idCity;
}
#Override
public String getName()
{
return name;
}
#Override
public void setName(String name)
{
this.name = name;
}
#Override
public ICountry getCountryId()
{
return countryId;
}
#Override
public void setCountryId(ICountry countryId)
{
this.countryId = (CountryDto) countryId;
}
}
#Entity
#Table(name = "CITY")
#JsonSerialize(as=City.class)
#JsonDeserialize(as=City.class)
public class City implements ICity
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_CITY")
private Integer idCity;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "NAME")
private String name;
#JoinColumn(name = "COUNTRY_ID", referencedColumnName = "ID_COUNTRY")
#ManyToOne(optional = false)
private Country countryId;
private static final long serialVersionUID = 1L;
public City()
{
}
public City(Integer idCity)
{
this.idCity = idCity;
}
public City(Integer idCity, String name)
{
this.idCity = idCity;
this.name = name;
}
#Override
public Integer getIdCity()
{
return idCity;
}
#Override
public void setIdCity(Integer idCity)
{
this.idCity = idCity;
}
#Override
public String getName()
{
return name;
}
#Override
public void setName(String name)
{
this.name = name;
}
#Override
public ICountry getCountryId()
{
return countryId;
}
#Override
public void setCountryId(ICountry countryId)
{
this.countryId = (Country) countryId;
}
#Override
public int hashCode()
{
int hash = 0;
hash += (idCity != null ? idCity.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object)
{
// TODO: Warning - this method won't work in the case the id fields are
// not set
if (!(object instanceof City))
{
return false;
}
City other = (City) object;
if ((this.idCity == null && other.idCity != null) || (this.idCity != null && !this.idCity.equals(other.idCity)))
{
return false;
}
return true;
}
#Override
public String toString()
{
return "com.neology.ebreeder.model.entities.City[ idCity=" + idCity + " ]";
}
}
And as You can see in the entity I have getters and setters using the shared interface, and I think that It could provoke problems, I thought to override the getters using the entity but I can't override the setters.
I cant do this:
#Override
public Country getCountryId()
{
return countryId;
}
But I can't do this :
#Override
public void setCountryId(Country countryId)
{
this.countryId = (Country) countryId;
}
Do you see a better solution or could you give me your point of view :)
thanks
Based on past experience, I do not think it is a good idea to use an interface that is shared between the DTO model and the JPA model.
You are in essence tightly coupling your DTO model to your JPA model with this approach.
I would rather have them loosely coupled and use a separate framework to copy between these two models. This will need to be powered by a meta model (could be derived from JPA) to walk and copy the data from one model to another based on the getters and setters.

hibernate session.persist(object) create more rows in database

Helo,
I have method
public void createContest(Contest c) {
session.beginTransaction();
session.persist(c);
session.getTransaction().commit();
logger.info("Contest saved successfully!");
}
I call it in one place with one object but this method save the object Contest to the database 4x. What could be the problem?
Here is Contest class:
#Entity
#Table(name = "Contest")
public class Contest implements java.io.Serializable {
#Id
#Column(name="CONTEST_ID", unique = true, nullable = false, precision = 5, scale = 0)
#GeneratedValue
private int id;
#Column(name="NAME")
private String name;
#Column(name="DATE_OF_EVENT")
private Date dateOfEvent;
#Column(name="REG_OPEN")
private Date regOpen;
#Column(name="REG_CLOSE")
private Date regClose;
#Column(name="REG_RESTRICTION")
private Boolean regRestriction;
#Column(name="CAPACITY")
private int capacity;
#OneToMany(mappedBy="upperContest")
private List<Contest> precontests;
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="UPPER_CONTEST_ID")
private Contest upperContest;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="MANAGING",
joinColumns={#JoinColumn(name="CONTEST_ID")},
inverseJoinColumns={#JoinColumn(name="PERSON_ID")})
private List<Person> managers;
#OneToMany(mappedBy="contest")
private List<Team> teams;
public Contest(String name, Date dateOfEvent, Date regOpen,
Date regClose, Boolean regRestriction) {
this.name = name;
this.dateOfEvent = dateOfEvent;
this.regOpen = regOpen;
this.regClose = regClose;
this.regRestriction = regRestriction;
this.precontests = new ArrayList<Contest>();
this.managers = new ArrayList<Person>();
this.teams = new ArrayList<Team>();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDateOfEvent() {
return dateOfEvent;
}
public void setDateOfEvent(Date dateOfEvent) {
this.dateOfEvent = dateOfEvent;
}
public Date getRegOpen() {
return regOpen;
}
public void setRegOpen(Date regOpen) {
this.regOpen = regOpen;
}
public Date getRegClose() {
return regClose;
}
public void setRegClose(Date regClose) {
this.regClose = regClose;
}
public Boolean getRegRestriction() {
return regRestriction;
}
public void setRegRestriction(Boolean regRestriction) {
this.regRestriction = regRestriction;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public Contest getUpperContest() {
return upperContest;
}
public void setUpperContest(Contest c) {
this.upperContest = c;
}
public List<Contest> getPrecontests() {
return precontests;
}
public void addPrecontest(Contest e) {
this.precontests.add(e);
}
public void addManager(Person p){
this.managers.add(p);
}
public void addTeam(Team t){
this.teams.add(t);
}
public List<Person> getManagers(){
return managers;
}
}
And now I have to add some text because of restrictions of stackoverflow. Such Cool. Very wow.
You haven't posted the code where you instantiate Contest and populate its fields, but upperContest is probably the cause. You have CascadeType.ALL on the mapping, so you probably are persisting one contest which has a upper contest, thus actually persisting two instances because of cascade setting. Can't know for sure without that code.

Hibernate: cant get the associated List retrieved

An entity X has a list of entity Y and the entity Y has an instance of entity Z.
The relation between X to Y is OneToMany and the relation between Y to Z is ManyToOne.
I want to retrieve X and have all the associated entities retrieved with them as well.
What HQL query do I write so that I get the whole chain retrieved all at once. At present its hibernateTemplate.find("from X").
or What annonations do I use for it?
X=ServiceProvider, Y=BusinessLocations.java, Z=State.java
I have the entities annotated below and I am having the whole chain persisted into database but when i try to retrieve the list of Y(BusinessLocation), I get
nothing.
What do I do join X with Y and Y with Z?
Below are the entities x, Y and Z.
ServiceProvider.java
#Entity
public class ServiceProvider implements Serializable{
private Long id;
private Set<BusinessLocation> businessLocations = new HashSet<BusinessLocation>();
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#OneToMany(mappedBy="serviceProvider", targetEntity=BusinessLocation.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public Set<BusinessLocation> getBusinessLocations() {
return businessLocations;
}
public void setBusinessLocations(Set<BusinessLocation> businessLocations) {
this.businessLocations = businessLocations;
}
public boolean equals(Object obj) {
if(!(obj instanceof ServiceProvider)) return false;
ServiceProvider other = (ServiceProvider) obj;
return new EqualsBuilder().append(businessLocations, other.businessLocations).isEquals();
}
}
BusinessLocation.java
#Entity
public class BusinessLocation implements Serializable{
private Long id;
private String address;
private String city;
private State state;
private String pincode;
private ServiceProvider serviceProvider;
public BusinessLocation() {
}
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="state_id")
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void setPincode(String pincode) {
this.pincode = pincode;
}
public String getPincode() {
return pincode;
}
#ManyToOne
#JoinColumn(name="serviceProvider_id")
public ServiceProvider getServiceProvider() {
return serviceProvider;
}
public void setServiceProvider(ServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
public boolean equals(Object obj) {
if( !(obj instanceof BusinessLocation)) return false;
BusinessLocation other = (BusinessLocation) obj;
return new EqualsBuilder().append(address, other.address).append(city, other.city).append(state, other.state).append(pincode,
other.pincode).append(serviceProvider, other.serviceProvider).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(address).append(city).append(state).append(pincode).append(serviceProvider).toHashCode();
}
}
State.java
#Entity
public class State implements Serializable {
private Long id;
private String abbreviatedName;
private String name;
private List<BusinessLocation> businessLocations;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAbbreviatedName() {
return abbreviatedName;
}
public void setAbbreviatedName(String abbreviatedName) {
this.abbreviatedName = abbreviatedName;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
#OneToMany(mappedBy="state", targetEntity=BusinessLocation.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public List<BusinessLocation> getBusinessLocations() {
return businessLocations;
}
public void setBusinessLocations(List<BusinessLocation> businessLocations) {
this.businessLocations = businessLocations;
}
public boolean equals(Object obj) {
if(! (obj instanceof State)) return false;
State other = (State) obj;
return new EqualsBuilder().append(abbreviatedName, other.abbreviatedName).append(name, other.name).append(businessLocations,
other.businessLocations).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(name).append(abbreviatedName).append(businessLocations).toHashCode();
}
}
Could someone help me out here?
Thanks
What about:
Query q - entityManager.createQuery("Select x from ServiceProvider x inner join x.businessLocations as y and inner join y.state as z where x.id = ?1");
I have the entities annotated below and I am having the whole chain persisted into database but when i try to retrieve the list of Y (BusinessLocation), I get... nothing
You should activate SQL logging to see what is happening and check the data because the annotation part looks correct:
the one-to-many between ServiceProvider and BusinessLocation is EAGER
the many-to-one between BusinessLocation and State is EAGER (by default)
So the whole chain should be retrieved eagerly. If this is not what is happening, you might want to check the data and the SQL, hence the suggestion.
As an alternative to EAGER associations, you could use a FETCH JOIN to prefetch the related data:
FROM ServiceProvider provider
INNER JOIN FETCH provider.businessLocations location
LEFT JOIN FETCH location.state
But note that JPA 1.0 does not allow nested join fetches in JPQL, this is Hibernate specific.
References
Hibernate Core Reference Guide
14.3. Associations and joins
JPA 1.0 specification
Section 9.1.22 "ManyToOne Annotation"
Section 4.4.5.3 "Fetch Joins"

Categories