I am trying to implement a tagging system in my database (MySQL V5.1.61), and then get that working in hibernate. Here are the relevant parts of my database:
And the data contained:
If I am doing this correctly, then 'nir' should have 3 tags associated with him, 'Food','Sorority', and 'Summer Internship'.
What I am having trouble with is implementing this relationship in hibernate (Using annotations):
The UserHibernate class (I'm using GWT so I need separate hibernate and DTO objects):
#Entity
#Table(name="user")
public class UserHibernate implements Serializable{
private int ID;
//removed fields for brevity
private Set<UserTagsHibernate> tags;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "uID")
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
/**
* #return the tags
*/
#OneToMany(mappedBy="user", fetch=FetchType.EAGER)
public Set<UserTagsHibernate> getTags() {
return tags;
}
/**
* #param tags the tags to set
*/
public void setTags(Set<UserTagsHibernate> tags) {
this.tags = tags;
}
}
The UserTagsHibernate Class:
#Entity
#Table(name="usertags")
public class UserTagsHibernate {
private int usertagsID;
private UserHibernate user;
private TagsHibernate tags;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="userforeignkey")
public UserHibernate getUser() {
return user;
}
public void setUser(UserHibernate userHibernate) {
this.user = userHibernate;
}
#OneToOne
#JoinColumn(name="tagforeignkey")
public TagsHibernate getTags() {
return tags;
}
public void setTags(TagsHibernate tagsHibernate) {
this.tags = tagsHibernate;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "usertagsID")
public int getUsertagsID() {
return usertagsID;
}
public void setUsertagsID(int usertagsID) {
this.usertagsID = usertagsID;
}
}
The TagsHibernate Class:
#Entity
#Table(name = "tags")
public class TagsHibernate {
private int tagID;
//removed for brevity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tagID")
public int getTagID() {
return tagID;
}
public void setTagID(int tagID) {
this.tagID = tagID;
}
}
The problem that I am having is that when I try and retrieve a user, here 'nir', he shows up three times. I believe it is because he has 3 tags, so for some reason, when I issue the query "session.createCriteria(UserHibernate.class).add(Restrictions.eq("username", "nir")).list();" I get a list of length 3. Any ideas why this is happening?
This problem pops up all the time when using the Criteria API...it's a known quirk. The workarounds are either to use HQL instead or to add a transformer that filters out the duplicates like so:
session.createCriteria(UserHibernate.class)
.add(Restrictions.eq("username", "nir"))
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
.list();
Related
I have the following classes:
#Entity(name = "focusoc_orbit")
#Data
public class OrbitAdapter extends Adapter{
#Id
private String id;
...
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
...
}
And,
#Entity(name = "focusoc_conjunction")
#Data
public class ConjunctionAdapter extends Adapter {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "target_id")
private OrbitAdapter target;
#ManyToOne
#JoinColumn(name = "chaser_id")
private OrbitAdapter chaser;
...
public OrbitAdapter getTarget(){
return target;
}
public void setTarget(OrbitAdapter target){
this.target = target;
}
public String getChaserId(){
return chaserId;
}
public void setChaser(OrbitAdapter chaser){
this.chaser = chaser;
}
...
}
Also I defined the Repository:
public interface ConjunctionRepository extends PagingAndSortingRepository<ConjunctionAdapter, Long> {
public ConjunctionAdapter findByTargetAndChaserAndTimeOfCloseApproach(String target, String chaser, Date timeOfCloseApproach);
}
When I try to make the call,
ConjunctionAdapter c = conjunctionRepository.findByTargetAndChaserAndTimeOfCloseApproach(targetId, chaserId, timeOfCloseApproach());
It returns the error:
org.hibernate.property.access.spi.PropertyAccessException: Error
accessing field [private java.lang.String
gcs.fds.focusoc.adapter.OrbitAdapter.id] by reflection for persistent
property [gcs.fds.focusoc.adapter.OrbitAdapter#id] : 02035A"
I tried a lot of differents solutions that I found but it does not work for me. Any help?
I solved it !
Changing the annotation #Data of the classes for #Table with the table name. So,
#Entity
#Table(name = "focusoc_orbit")
public class OrbitAdapter extends Adapter{
#Id
private String id;
...
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
...
}
And,
#Entity
#Table(name = "focusoc_conjunction")
public class ConjunctionAdapter extends Adapter {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "target_id")
private OrbitAdapter target;
#ManyToOne
#JoinColumn(name = "chaser_id")
private OrbitAdapter chaser;
...
public OrbitAdapter getTarget(){
return target;
}
public void setTarget(OrbitAdapter target){
this.target = target;
}
public String getChaserId(){
return chaserId;
}
public void setChaser(OrbitAdapter chaser){
this.chaser = chaser;
}
...
}
And doing this it returns me the error,
java.lang.IllegalArgumentException: Parameter value [02035A] did not
match expected type [gcs.fds.focusoc.adapter.OrbitAdapter (n/a)]
So I added the annotation #Query into the Repository to specify which things has to be searched in the database,
public interface ConjunctionRepository extends PagingAndSortingRepository<ConjunctionAdapter, Long> {
#Query("SELECT c FROM ConjunctionAdapter c WHERE c.target.id = :target AND c.chaser.id = :chaser AND c.timeOfCloseApproach = :timeOfCloseApproach")
public ConjunctionAdapter findByTargetAndChaserAndTimeOfCloseApproach(String target, String chaser, Date timeOfCloseApproach);
}
And solved!
I am trying to perform a fetch using Criteria API with restrictions on the variable of an embedded entity. But I am getting the below error,
org.hibernate.QueryException: could not resolve property: cpm of: org.sorabh.SystemEnt
Below is my SystemEnt entity,
#Entity
#Table(name="system")
public class SystemEnt implements Serializable{
#Id
#Column(name="pmTpId", nullable = false)
private int id;
#Column(name="pmMeasureId", nullable = false)
private int pmMeasureId;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="pmMeasureId", insertable=false, updatable=false)
private Cpm cpm;
private String mapper;
public int getId() {
return id;
}
public int getPmMeasureId() {
return pmMeasureId;
}
public Cpm getCpm() {
return cpm;
}
public String getMapper() {
return mapper;
}
public void setId(int id) {
this.id=id;
}
public void setPmMeasureId(int pmMeasureId) {
this.pmMeasureId=pmMeasureId;
}
public void setCpm(Cpm cpm) {
this.cpm=cpm;
}
public void setMapper(int mapper) {
this.mapper=mapper;
}
My CpmMeas contains
#Entity
#Table(name="cpm")
public class Cpm implements Serializable{
#Id
#Column(name="pmMeasureId", nullable = false)
private int pmMeasureId;
#Column(nullable=false)
private int pmGranularity
//getters and setters
public void setPmMeasureId(int pmMeasureId) {
this.pmMeasureId=pmMeasureId;
}
public int getPmMeasureId() {
return pmMeasureId;
}
public void setPmGranularity(int pmGranularity) {
this.pmGranularity=pmGranularity;
}
public int getPmGranularity() {
return pmGranularity;
}
I do the below in my serviceImpl:
Criteria cr = session.createCriteria(SystemEnt.class);
cr.add(Restrictions.like("mapper", "Test", MatchMode.START));
cr.add(Restrictions.eq("cpm.pmGranularity", 1));
I have tried using aliases as suggested in a few other posts on SO, but it doesn't seem to solve the issue.
Also, it is not having issues in resolving mapper, i.e only the nested entities and corresponding variables are not getting resolved.
Use alias , as
Criteria cr = session.createCriteria(SystemEnt.class);
cr.createAlias("cpm","cpm");
cr.add(Restrictions.like("mapper", "Test", MatchMode.START));
cr.add(Restrictions.eq("cpm.pmGranularity", 1));
anyway the field mapper should be a String, in your sample code it is defined as an int.
I tested and without a doubt it works fine.
I am facing some weird issue when using Hibernate one to many and many to one mapping and returning data using JSON to my swing client from rest web service.
When my web service is returning salesOrder object. i have checked that it does contains orderlines objects set. But, if i open one of the orderLine object, it again has sales order object.
This chaining is causing issue as client side that infinite string of json is being returned.
Like below...
[
{
"salesOrderNumber":"1",
"customerCode":"1",
"totalPrice":50.0,
orderLines":
[
{
"salesOrderNumber":"1",
"productCode":"2",
"quantity":1,
"salesOrder":{"salesOrderNumber":"1","customerCode":"1","totalPrice":50.0,"orderLines":[{"salesOrderNumber":"1","productCode":"2","quantity":1,"salesOrder":{"salesOrderNumber":"1","customerCode":"1","totalPrice":50.0,"orderLines":
.............................................
...............................
I have tried to set #JSONIgnore as i don't want those to be sent to client, but, it didn't help.
My Two entities are like below:
#Entity
#Table(name = "salesorder")
//#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class SalesOrder implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "SalesOrderNumber", unique = true, nullable = false)
private String salesOrderNumber;
#Column(name = "CustomerCode")
private String customerCode;
#Column(name = "TotalPrice")
private double totalPrice;
#JsonIgnore
#OneToMany(fetch = FetchType.EAGER, mappedBy="salesOrder",cascade=CascadeType.ALL)
private Set<OrderLines> orderLines = new HashSet<OrderLines>();
public Set<OrderLines> getOrderLines() {
return orderLines;
}
public void setOrderLines(Set<OrderLines> orderLines) {
this.orderLines = orderLines;
}
public String getSalesOrderNumber() {
return salesOrderNumber;
}
public void setSalesOrderNumber(String salesOrderNumber) {
this.salesOrderNumber = salesOrderNumber;
}
public String getCustomerCode() {
return customerCode;
}
public void setCustomerCode(String customerCode) {
this.customerCode = customerCode;
}
public double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(double totalPrice) {
this.totalPrice = totalPrice;
}
}
#Entity
#Table(name = "orderlines")
//#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class OrderLines implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "SalesOrderNumber", unique = true, nullable = false)
private String salesOrderNumber;
#Id
#Column(name = "ProductCode")
private String productCode;
#Column(name = "Quantity")
private int quantity;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="SalesOrderNumber")
private SalesOrder salesOrder;
public SalesOrder getSalesOrder() {
return salesOrder;
}
public void setSalesOrder(SalesOrder salesOrder) {
this.salesOrder = salesOrder;
}
public String getSalesOrderNumber() {
return salesOrderNumber;
}
public void setSalesOrderNumber(String salesOrderNumber) {
this.salesOrderNumber = salesOrderNumber;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
It's a really bad idea to send your entities as json, as it will couple your clients to the internal representation of your system... and bad hacks come from this. If you do want to do this regardless and suffer later (or let one of your future colleagues suffer and curse you), keep reading.
The reason why it doesn't work is because Hibernate creates proxies from the objects that it gets from the DB, and the annotations are lost. There is a Jackson extension that will take care of this jackson-datatype-hibernate, but please don't do it (unless your app is trivial and will never change)
I have a many-to-many relationship between EnfInspPrgm entity and EnfInspPmSc entity.
Here are the entity classes
public class EnfInspPrgm implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="PRM_ID")
private long prmId;
#Column(name="AREA_ID")
private long areaId;
//bi-directional many-to-one association to EnfInspPmSc
#OneToMany(mappedBy="enfInspPrgm")
private List<EnfInspPmSc> enfInspPmScs;
public EnfInspPrgm() {
}
public long getPrmId() {
return this.prmId;
}
public void setPrmId(long prmId) {
this.prmId = prmId;
}
public long getAreaId() {
return this.areaId;
}
public void setAreaId(long areaId) {
this.areaId = areaId;
}
public List<EnfInspPmSc> getEnfInspPmScs() {
return this.enfInspPmScs;
}
public void setEnfInspPmScs(List<EnfInspPmSc> enfInspPmScs) {
this.enfInspPmScs = enfInspPmScs;
}
public EnfInspPmSc addEnfInspPmSc(EnfInspPmSc enfInspPmSc) {
getEnfInspPmScs().add(enfInspPmSc);
enfInspPmSc.setEnfInspPrgm(this);
return enfInspPmSc;
}
public EnfInspPmSc removeEnfInspPmSc(EnfInspPmSc enfInspPmSc) {
getEnfInspPmScs().remove(enfInspPmSc);
enfInspPmSc.setEnfInspPrgm(null);
return enfInspPmSc;
}
}
#Entity
#Table(name="ENF_INSP_PM_SC")
public class EnfInspPmSc implements Serializable {
private static final long serialVersionUID = 1L;
//bi-directional many-to-one association to InspectionSource
#ManyToOne(optional=false)
#JoinColumn(name="ENF_INSP_SOURCE_ID")
private InspectionSource inspectionSource;
#Column(name="ENF_INSP_PRM_SRC_ID")
private long enfInspPrmSrcId;
#ManyToOne
#JoinColumn(name="PRM_ID")
private EnfInspPrgm enfInspPrgm;
public EnfInspPmSc() {
}
}
#Entity
#Table(name = "REF_ENF_INSP_SOURCE")
public class InspectionSource implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ENF_INSP_SOURCE_ID")
private Integer id;
// bi-directional many-to-one association to User
#ManyToOne
#JoinColumn(name = "CREATED_BY_USER_ID")
private User createdUser;
// bi-directional many-to-one association to User
#ManyToOne
#JoinColumn(name = "MODIFIED_BY_USER_ID")
private User modifiedUser;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public User getCreatedUser() {
return createdUser;
}
public void setCreatedUser(User createdUser) {
this.createdUser = createdUser;
}
public String getCreatedByName() {
return createdByName;
}
public void setCreatedByName(String createdByName) {
this.createdByName = createdByName;
}
public User getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(User modifiedUser) {
this.modifiedUser = modifiedUser;
}
}
I am creating a JPA repository for selecting the EnfInspPrgm entities. But itis causing an error
Invalid identifier for enfInspPrgm, ENF_INSP_SOURCE_ID
Please help me and resolve this issue.
Your code seems missing some declaration. For example EnfInspPrgm is not annotatted with #Entity.
Also, In EnfInspPmSc you didn't put getter and setter for the fields. Put getEnfInspPrgm and setEnfInspPrgm. Do the same for the other fields.
Note that JPA declartions will be translated in all cases into native sql. The owner of the relationship is always the many side which has a FK to the one side. So, If EnfInspPmSc don't have those setters and getters you won't be able to get access to any data in both tables.
I have two DTO objects say A and B which are having getters and setters and are used to take data from the database. The problem is when I am calling A, B gets called and B again points itself to A and a cycle is created.
I cannot ignore/hide the method which is creating the cycle. I need to take the whole data of A and B.
Is there any way to achieve it ?
Please help
This is my code which is causing the problem. This is application DTO which is calling environment DTO
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
And this is environment DTO which is calling the application DTO
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Here cycle is getting created
This is my rest call which will give result in XML format and I think while creating XML cycle is getting created
#GET
#Path("/get")
#Produces({MediaType.APPLICATION_XML})
public List<ApplicationDTO> getAllApplications(){
List<ApplicationDTO> allApplication = applicationService.getAllApplication();
return allApplication;
}
This is the Application DTO class
#Entity
#Table(name="application")
#org.hibernate.annotations.GenericGenerator(
name ="test-increment-strategy",strategy = "increment")
#XmlRootElement
public class ApplicationDTO implements Serializable {
#XmlAttribute
public Long appTypeId;
private static final long serialVersionUID = -8027722210927935073L;
private Long applicationId;
private String applicationName;
private ApplicationTypeDTO applicationType;
private String applicationDescription;
private Integer owner;
private Integer createdBy;
private Integer assignedTo;
private Date createTime;
private Date modifiedTime;
private Set<EnvironmentDTO> environment;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "applicationId")
public Long getApplicationId() {
return applicationId;
}
private void setApplicationId(Long applicationId) {
this.applicationId = applicationId;
}
#Column(name = "applicationName")
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
#ManyToOne(targetEntity=ApplicationTypeDTO.class
,fetch = FetchType.LAZY
)
#JoinColumn(name="applicationType")
public ApplicationTypeDTO getApplicationType() {
return applicationType;
}
public void setApplicationType(ApplicationTypeDTO applicationType) {
this.applicationType = applicationType;
}
#Column(name = "description")
public String getApplicationDescription() {
return applicationDescription;
}
public void setApplicationDescription(String applicationDescription) {
this.applicationDescription = applicationDescription;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Column(name = "createdBy")
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
#Column(name = "assignedTo")
public Integer getAssignedTo() {
return assignedTo;
}
public void setAssignedTo(Integer assignedTo) {
this.assignedTo = assignedTo;
}
#Column(name = "createTime")
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#Column(name = "modifiedTime")
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
This is the Environment DTO class
#Entity
#Table(name="environment")
#org.hibernate.annotations.GenericGenerator(
name = "test-increment-strategy",
strategy = "increment")
#XmlRootElement
public class EnvironmentDTO implements Serializable {
#XmlAttribute
public Long envTypeId;
#XmlAttribute
public Long appId;
private static final long serialVersionUID = -2756426996796369998L;
private Long environmentId;
private String environmentName;
private EnvironmentTypeDTO environmentType;
private Integer owner;
private Date createTime;
private Set<InstanceDTO> instances;
private ApplicationDTO application;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "envId")
public Long getEnvironmentId() {
return environmentId;
}
private void setEnvironmentId(Long environmentId) {
this.environmentId = environmentId;
}
#Column(name = "envName")
public String getEnvironmentName() {
return environmentName;
}
public void setEnvironmentName(String environmentName) {
this.environmentName = environmentName;
}
#ManyToOne(targetEntity=EnvironmentTypeDTO.class)
#JoinColumn(name = "envType")
public EnvironmentTypeDTO getEnvironmentType() {
return environmentType;
}
public void setEnvironmentType(EnvironmentTypeDTO environmentType) {
this.environmentType = environmentType;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Temporal(TemporalType.DATE)
#Column(name = "createTime")
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#OneToMany(mappedBy="environment", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public Set<InstanceDTO> getInstances() {
return instances;
}
public void setInstances(Set<InstanceDTO> instances) {
this.instances = instances;
}
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
//#XmlTransient
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Your object graph is cyclic. There is nothing intrinsically wrong with that, and it is a natural consequence of using JPA.
Your problem is not that your object graph is cyclic, but that you are encoding it in a format which cannot handle cycles. This isn't a Hibernate question, it's a JAXB question.
My suggestion would be to stop JAXB from attempting to marshal the application property of the EnvironmentDTO class. Without that property the cyclic graph becomes a tree. You can do this by annotating that property with #XmlTransient.
(confession: i learned about this annotation by reading a blog post by Mr Doughan, which i came across after reading his answer to this question!)
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers the #XmlInverseReference extension to handle this use case. Below is an example of how to apply this mapping on two entities with a bidirectional relationship.
Customer
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
My advice is not exposing your JPA entity class to your webservices. You can create different POJO class and convert your JPA entity to the POJO. For example:
this is your JPA entity
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
you should use this class for your webservices:
public class CustomerModel{
private long id;
//you can call different WS to get the Address class, or combine to this model
public void setFromJpa(Customer customer){
this.id = customer.id;
}
}