I'm trying to get Hibernate #OneToOne annotation working with 2 classes, Hito and Portada. Portada table has the foreign key of Hito, an int attribute called hito.
My entities looks like this:
Hito:
#Entity
#Table(name = "hito")
public class Hito implements Serializable {
//...other attributes
private Portada portada;
//...getters and setters from other attributes
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "hito")
public Portada getPortada(){ return portada;}
public void setPortada(Portada portada){ this.portada = portada;}
}
Portada:
#Entity
#Table(name = "portada")
public class Portada {
//...other attributes
private Hito hito;
//...getters and setters from other attributes
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "hito")
public Hito getHito() {return hito;}
public void setHito(Hito hito) {this.hito = hito;}
}
When I call hito.getPortada(), I expect a Portada object, but it returns null.
Any suggestions?
I tried to reproduce your problem with code:
#MappedSuperclass
public abstract class BaseEntity {
#Id #GeneratedValue
private Long id;
#Version
private long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
#Entity
#Table(name = "portada")
public class Portada extends BaseEntity {
//...other attributes
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "hito")
private Hito hito;
//...getters and setters from other attributes
public Hito getHito() {return hito;}
public void setHito(Hito hito) {this.hito = hito;}
}
#Entity
#Table(name = "hito")
public class Hito extends BaseEntity implements Serializable {
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "hito")
private Portada portada;
public Portada getPortada(){ return portada;}
public void setPortada(Portada portada){ this.portada = portada;}
}
// app:
Portada p = new Portada();
Hito h = new Hito();
p.setHito(h);
h.setPortada(p);
entityManager.persist(h);
entityManager.flush();
entityManager.clear();
Hito h2 = entityManager.find(Hito.class, h.getId());
System.out.println(h2.getPortada().toString());
tx.commit();
The last find generated sql:
select
hito0_.id as id1_4_0_,
hito0_.version as version2_4_0_,
portada1_.id as id1_7_1_,
portada1_.version as version2_7_1_,
portada1_.hito as hito3_7_1_
from
hito hito0_
left outer join
portada portada1_
on hito0_.id=portada1_.hito
where
hito0_.id=?
Everything worked for me...
EDIT: Only difference is that I like to put mapping attributes on fields instead of properties but it doesn't matter in this problem. Please check if you add both of your classes to persistance.xml or hibernate config.
Related
I have a two entities relationship, I believe that I did all fine, but when I try to run my application, Payara is launching this error:
Caused by: Exception [EclipseLink-7154] (Eclipse Persistence Services - 2.7.4.payara-p2): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [retenciones] in entity class [class komp.model.ChequePropio] has a mappedBy value of [chequepropio] which does not exist in its owning entity class [class komp.model.RetencionChp]. If the owning entity class is a #MappedSuperclass, this is invalid, and your attribute should reference the correct subclass.
Here one of entities causing the problem:
#Entity
#Table(name = "chequepropio", uniqueConstraints = {
#UniqueConstraint(name = "ukchequepropio", columnNames = {"idbanco", "idempresa", "idtipo", "numero"})})
public class ChequePropio implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "chequepropio")
private List<RetencionChp> retenciones;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
The other entities:
#Entity
#Table(name = "retencionchp")
public class RetencionChp implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "idcheque", insertable = true, updatable = true)
private ChequePropio chequePropio;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ChequePropio getChequePropio() {
return chequePropio;
}
public void setChequePropio(ChequePropio chequePropio) {
this.chequePropio = chequePropio;
}
}
I can't see any error in the relationship, somebody could say me what is wrong?
Thanks in advance!
Fernando
The error is self-explanatory:
The attribute [retenciones] in entity class [class
komp.model.ChequePropio] has a mappedBy value of [chequepropio] which
does not exist in its owning entity class
Mind the keyword chequepropio. You don't have a chequepropio property in RetencionChp. You have a chequePropio instead. Mind the uppercase P. The following should solve it (mappedBy = "chequePropio")).
#Entity
#Table(name = "chequepropio", uniqueConstraints = {
#UniqueConstraint(name = "ukchequepropio", columnNames = {"idbanco", "idempresa", "idtipo", "numero"})})
public class ChequePropio implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "chequePropio")
private List<RetencionChp> retenciones;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
I have the following 2 classes:
#Entity
#Table(name = "TableA")
public class EntityA
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private final Integer id = null;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "BId")
private EntityB b;
public EntityA(EntityB b)
{
this.b = b;
}
}
#Entity
#Table(name = "TableB")
public class EntityB
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private final Integer id = null;
#OneToOne(mappedBy = "b")
private final EntityA a = null;
}
When I do
session.save(new EntityA(new EntityB());
the database only inserts a record in TableA and leaves the column that references TableB as NULL
If I first insert b, then a, it works, but it should work with a single call too.
Other questions/answers mention that the annotations are not correct, but I see no difference between mine and the provided solutions.
I also tried adding the CascadeType.PERSIST on both #OneToOne annotations, but that didnt work either.
In jpa, default Cascade setting is NONE ... thus the entities in relationships (B in your case) is not inserted (persisted) by default ... You need to annotate the relationship with
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
First of all you must delete final keyword from your entities.
Try this one:
#Entity
#Table(name = "TableA")
class EntityA {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Integer id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "BId")
private EntityB b;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EntityB getB() {
return b;
}
public void setB(EntityB b) {
this.b = b;
}
public EntityA(EntityB b) {
this.b = b;
b.setA(this);
}
}
#Entity
#Table(name = "TableB")
class EntityB {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Integer id;
#OneToOne(mappedBy = "b")
private EntityA a;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EntityA getA() {
return a;
}
public void setA(EntityA a) {
this.a = a;
}
}
I am using spring boot, hibernate and H2 database
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext app = SpringApplication.run(DemoApplication.class, args);
ServiceCascade bean = app.getBean(ServiceCascade.class);
bean.save();
}
}
#Service
class ServiceCascade {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void save() {
EntityA entityA = new EntityA(new EntityB());
entityManager.persist(entityA);
}
}
The following logs show that the two entities are inserted correctly
org.hibernate.SQL : insert into tableb (id) values (null)
org.hibernate.SQL : insert into tablea (id, bid) values (null, ?)
o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
I have following JPA entity structure.
#Entity
#Table(name = "PARENT_DETAILS")
class Parent{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "parent_details_seq")
#SequenceGenerator(name = "parent_details_seq", sequenceName = "PARENT_DETAILS_SEQ", allocationSize = 1)
#Column(name = "PARENT_ID")
private long parentId;
#Column(name = "PARENT_NAME")
private String parentName;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "childPK.parent", cascade = CascadeType.ALL)
private Set<Child> child;
//setters and getters
}
#Entity
#Table(name = "CHILD_DETAILS")
public class Child {
private ChildPK childPK;
public void setProgramProcessesPK(ChildPK childPK) {
this.childPK = childPK;
}
#EmbeddedId
public ChildPK getChildPK() {
return childPK;
}
}
#Embeddable
public class ChildPK implements Serializable {
private static final long serialVersionUID = 1L;
private Parent parent;
private long childId;
#Column(name = "CHILDID")
public long getChildId() {
return childId;
}
public void setChildId(long childId) {
this.childId = childId;
}
#ManyToOne
#JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID", nullable = false)
public ParentDetails getParent() {
return parent;
}
}
I want to write a JPA query which will return the PARENT_NAME and the count of all children for a given parent_id.
Tthe only solution I can think of is joining and writing a complex criteria query.
I cannot think of a way to get the result using a simple JPA query.
Is there an easier way to do this?
Have you tried SIZE? Something like "Select parent.parentName, Size(parent.child) from Parent parent" might work.
You can use JPA Named Query such as this:
private static class ParentChildsNumber {
public String parentName;
public Integer childsNum;
public ParentChildsNumber(String parentName, Integer childsNum) {
this.parentName = parentName;
this.childsNum = childsNum;
}
}
#NamedQuery(name="getParentChildsNumberQuery", query="SELECT NEW ParentChildsNumber(p.parentName, SIZE(p.child)) FROM Parent p WHERE p.parentId = :parentId GROUP BY p.parentId, p.parentName")
Use it in your code in the following way:
#PersistenceContext(unitName="YourPersistentUnit")
private EntityManager em;
em.createNamedQuery("getParentChildsNumberQuery", ParentChildsNumber.class).setParameter("parentId", parentId).getSingleResult();
I have a one to many relationship between Father and Son. I am working with JPA Repository.
If I delete a son with id 54 and then get all the sons again :
sonRepository.delete(id);
List<Son> sons = sonRepository.findAll();
I am getting this error :
javax.persistence.EntityNotFoundException: Unable to find
com.myapp.domain.Son with id 54
My entities :
#Entity
#Table(name = "T_FATHER")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class FAther implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "father", fetch = FetchType.EAGER)
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Son> sons = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<Son> getSons() {
return sons;
}
public void setSons(Set<Son> sons) {
this.sons = sons;
}
}
#Entity
#Table(name = "T_SON")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Son implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private Father father;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Father getFather() {
return father;
}
public void setFather(Father father) {
this.father = father;
}
}
This error seems to happen only when the son is already in database or inserted in database via sql (not with the application). If the son is added via the application :
sonRepository.save(son);
I don't have any problem. The problem happens again if I restart the server. It 's like the problem appears if the addSon and deleteSon are not done in the same session (same server instance).
I don't understand what I am doing wrong. If someone can help me on this...
Thank you.
It seems a bug to me in the caching mechanism. I would report it to Spring. As a working workaround the OP confirmed the removing #Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE).
I followed this tutorial to implement in my domain model a many-to-many relationship with an extra column. It works great but I'm unable to create a criteria to query a field within the left side of my relation.
Taking this code
#Entity
#Table( name = "projects")
public class Project implements Cloneable, Serializable{
private Long id;
private String name;
private Set<ProjectOrganization> projectOrganizations = new HashSet<ProjectOrganization>(0);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name", length = 255, nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.project")
#Cascade(value = { CascadeType.ALL })
public Set<ProjectOrganization> getProjectOrganizations() {
return this.projectOrganizations;
}
public void setProjectOrganizations(Set<ProjectOrganization> organizationProjects) {
this.projectOrganizations = organizationProjects;
}
}
#Entity
#Table(name = "projects_has_organizations")
#AssociationOverrides({ #AssociationOverride(name = "pk.project", joinColumns = #JoinColumn(name = "projects_id")),
#AssociationOverride(name = "pk.organization", joinColumns = #JoinColumn(name = "organizations_id"))
})
public class ProjectOrganization implements Cloneable, Serializable {
private ProjectOrganizationPK pk = new ProjectOrganizationPK();
private OrganizationRolesEnum role;
public ProjectOrganization() {
}
#Transient
public Organization getOrganization() {
return getPk().getOrganization();
}
public void setOrganization(Organization organization) {
getPk().setOrganization(organization);
}
#EmbeddedId
public ProjectOrganizationPK getPk() {
return pk;
}
public void setPk(ProjectOrganizationPK pk) {
this.pk = pk;
}
#Transient
public Project getProject() {
return getPk().getProject();
}
public void setProject(Project project) {
getPk().setProject(project);
}
#Enumerated(EnumType.STRING)
#Column(nullable = false, length = 50)
public OrganizationRolesEnum getRole() {
return role;
}
public void setRole(OrganizationRolesEnum role) {
this.role = role;
}
}
#Embeddable
public class ProjectOrganizationPK implements Cloneable, Serializable {
/** Generated serial version UID */
private static final long serialVersionUID = -4534322563105003365L;
private Organization organization;
private Project project;
#ManyToOne
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
#ManyToOne
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
#Entity
#Table(name = "organizations")
public class Organization implements Cloneable, Serializable {
private Long id;
private String name;
private Set<ProjectOrganization> projectOrganizations = new HashSet<ProjectOrganization>(0);
public Organization() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
#Override
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name", nullable = false, length = 255)
#NotNull(message = "A name is required!")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.organization")
public Set<ProjectOrganization> getProjectOrganization() {
return this.projectOrganizations;
}
public void setProjectOrganization(Set<ProjectOrganization> projectOrganizations) {
this.projectOrganizations = projectOrganizations;
}
}
I want is to create a criteria to select a Project which has an organization with a requested name.
final Criteria crit = getSession().createCriteria(Project.class);
crit.createCriteria("projectOrganizations", "projectOrganization").
createAlias("pk.organization", "organization").
add( Restrictions.like("organization.name", "TEST"));
But when i run this code i have this error
2012-10-19 10:38:43,095 ERROR [org.hibernate.util.JDBCExceptionReporter] Unknown column 'organizati2_.name' in 'where clause'
and the sql query generated by hibernate is incomplete, doesn't join projects_has_organizations.organization with organization.id.. So it can't find column organization.name
SELECT
....
FROM
projects this_
INNER JOIN projects_has_organizations projectorg1_ ON this_.id = projectorg1_.projects_id
WHERE
projectorg1_.role =?
AND organizati2_. NAME LIKE ?
ORDER BY
this_.publish_date DESC
What's wrong with this code? How can i build query using criteria ?
I suspect that the problem is due to lazy fetching, try explicitly telling hibernate to eagerly fetch the property you need. This is done with the method
.setFetchMode("propertyName", FetchMode.EAGER)
So, in otherwords, try eagerly fetch the organisation property :)