I have defined 3 classes as follows:
I tried to generate the schema in MySQL.
It won't succeed. But the wired thing is after I have renamed one class, CerifLink--->MyLink, it works.
Can anyone give a reasonable explanation?
package org.epos.grdb.jpa.entity;
import java.io.Serializable;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
#Entity
public class CerifLink implements Serializable {
/**
*
*/
private static final long serialVersionUID = -9162577962410473641L;
#Id
#ManyToOne
#JoinColumns(value = { #JoinColumn(referencedColumnName = "id"),
#JoinColumn(referencedColumnName = "cfid") }, foreignKey = #ForeignKey(value = ConstraintMode.CONSTRAINT, foreignKeyDefinition = "foreign key (`cfClassId`) references `cfClass` (`cfClassId`)"))
private Class clazz;
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
}
package org.epos.grdb.jpa.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class MyEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5185624925049306788L;
#Id
protected String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
package org.epos.grdb.jpa.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#Entity
public class Class implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1573828246619342971L;
#Id
protected String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Id
#ManyToOne
#JoinColumn(name = "cfid", referencedColumnName = "id")
private MyEntity classSchema;
public MyEntity getClassSchema() {
return classSchema;
}
public void setClassSchema(MyEntity classSchema) {
this.classSchema = classSchema;
}
}
But the wired thing is after I have renamed one class, CerifLink--->MyLink, it works.
Can anyone give a reasonable explanation?
Yes class name matter, you should check the NamingStrategy of Hibernate, you can modify it depending on the hibernate version you are using, but for what are you doing I guess you can use the annotation table and assign a specific name
#Table(name = "myname")
Related
I am using Spring Boot, and have the following Entity definitions (abridged):
package com.vw.asa.entities;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.PreUpdate;
import javax.validation.constraints.NotNull;
public abstract class CmsModel extends Model {
#Basic(optional = false)
#NotNull
#Column(name = "is_active")
private short isActive;
public short getIsActive() {
return isActive;
}
public void setIsActive(short isActive) {
this.isActive = isActive;
}
public void setIsActive(String isActive) {
if (isActive.equals("true")) {
this.isActive = IS_TRUE;
} else if (isActive.equals("1")) {
this.isActive = IS_TRUE;
} else {
this.isActive = IS_FALSE;
}
}
}
Then I have several models which extend this 'base' model, following this flavor:
package com.vw.asa.entities.cms;
import com.vw.asa.entities.CmsModel;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;
/**
* #author Barry Chapman
*/
#Entity
#Table(name = "cms_extra_questions", schema = "asa")
public class CmsExtraQuestions extends CmsModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
...
}
When I initialize an instance of CmsExtraQuestions as a result of a hibernate query, if I call setActive(true) on the object, it has no effect on the members of that object. When I copy the setters and getters from the CmsModel base class into the CmsExtraQuestions class, it works fine.
$entity = new CmsExtraQuestions();
$entity->setActive(true);
Why does this not set the member properties of the instantiated object when calling the extended setter? If this is normal - is there a way to add these properties and member functions to the base model so that they can be inherited also?
Rookie mistake, I forgot to add #MappedSuperClass to the inherited model, CmsModel. That allows JPA to map the properties from that class as though they were defined in the model that was inheriting that base class.
#MappedSuperClass
public abstract class CmsModel extends Model {
#Basic(optional = false)
#NotNull
#Column(name = "is_active")
Here is the class of the object I am trying to map:
package com.agent.module.entities;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
#Entity
#Getter #Setter #NoArgsConstructor
#Accessors
public class Accommodation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String location;
#ManyToOne(optional=false, fetch=FetchType.EAGER)
private AccommodationType type;
private String description;
private String name;
#OneToMany(orphanRemoval=true, fetch=FetchType.EAGER)
#Cascade(CascadeType.ALL)
private Set<Document> images;
private Integer capacity;
#ManyToMany(fetch=FetchType.EAGER)
private Set<AdditionalService> additionalServices;
#OneToMany(orphanRemoval=true, fetch=FetchType.EAGER)
#Cascade(CascadeType.ALL)
private Set<PricePlan> pricePlan;
#ManyToOne(optional=false, fetch=FetchType.LAZY)
private Agent agent;
#OneToMany(orphanRemoval=true, mappedBy="accommodation", fetch=FetchType.EAGER)
#Cascade(CascadeType.ALL)
private Set<Restriction> restrictions;
#ManyToOne(fetch=FetchType.EAGER)
private Category category;
#Override
public String toString() {
return "Name: "+name+"\n"+"Agent PIB: "+agent.toString()+"\n";
}
}
And here is my DTO object:
package com.agent.module.dto;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Getter #Setter #NoArgsConstructor
#XmlRootElement
public class AccommodationView {
private Long id;
private String location;
private String typeName;
private String description;
private String name;
private List<String> imagesPath;
private Integer capacity;
private List<String> additionalServicesName;
private List<PricePlanView> pricePlan;
private String agentUsername;
private List<RestrictionView> restrictions;
private String categoryName;
#Override
public String toString() {
return "ID: "+id+"\n"+"Type: "+typeName+"\n"+"Description: "+description+"\n"+"Category: "+categoryName+"\n"+"Name: "+name+"\n";
}
}
When I open my Postman and try to get all the Accommodation objects from MySQL database, I actually want to get DTO objects, and in order to do that I am using ModelMapper. But for some reason every time I try to map Accommodation to AccommodationView, I get Null in return. Here is the class where I am trying to perform the mapping:
#RestController
#RequestMapping(value = "/accommodation")
public class AccommodationController {
#Autowired
AccommodationRepo accommodationRepo;
#Autowired
ModelMapper mapper;
#RequestMapping(value="/all",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
ResponseEntity<List<AccommodationView>> getAll(){
List<Accommodation> accommodations = accommodationRepo.findAll();
List<AccommodationView> accommodationViewList= new ArrayList<AccommodationView>();
for(Accommodation accommodation : accommodations) {
System.out.println(accommodation);
System.out.println(convertToDto(accommodation));
accommodationViewList.add(convertToDto(accommodation));
}
return new ResponseEntity<List<AccommodationView>>(accommodationViewList, HttpStatus.OK);
}
private AccommodationView convertToDto(Accommodation accommodation) {
return mapper.map(accommodation, AccommodationView.class);
}
private Accommodation convertToEntity(AccommodationView accommodationView) {
return mapper.map(accommodationView, Accommodation.class);
}
}
Here is the output I get when I hit the method:
Name: Test
Agent PIB: 2308995710368
ID: null
Type: null
Description: null
Category: null
Name: null
First part of the output is from Accommodation object, and second part of the output is from AccommodationView object. If anyone has any idea whats going on I would really appreciate the help.
you have to generate public setters functions for the target class, in your case (Accommodation Entity). elsewise the Modelmapper cannot access the private fields of your class to set their values.
I want to return a tuple of Parent.id field and List<Child.id>.
Parent:
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
#Entity
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "id")
private Long parentId;
//we actually use Set and override hashcode&equals
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> children = new ArrayList<>();
public void addChild(Child child) {
child.setParent(this);
children.add(child);
}
public void removeChild(Child child) {
child.setParent(null);
children.remove(child);
}
public Long getParentId() {
return id;
}
public List<Child> getReadOnlyChildren() {
return Collections.unmodifiableList(children);
}
}
Child:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
#Entity
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "id")
private Long childId;
#ManyToOne
#JoinColumn(name = "id")
private Parent parent;
public Long getChildId() {
return id;
}
public Parent getParent() {
return parent;
}
/**
* Only for usage in {#link Parent}
*/
void setParent(final Parent parent) {
this.parent = parent;
}
}
The Spring Data Projection:
import java.util.List;
interface IdAndChildrenIds {
Long getParentId();
List<ChildId> getChildren();
}
interface ChildId {
Long getChildId();
}
The ParentRepository this is where problems begin:
import org.springframework.data.repository.CrudRepository;
public interface ParentRepository extends CrudRepository<Parent, Long> {
IdAndChildrenIds findIdAndChildrenIdsById(Long id);
}
But that doesn't work because the property doesn't comply with JavaBean standard (getter getReadOnlyChildren instead of getChildren), so I configured ObjectMapper to recognize private fields:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
#Configuration
#EnableWebMvc
public class HibernateConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper mapper = new Jackson2ObjectMapperBuilder().build();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
converters.add(new MappingJackson2HttpMessageConverter(mapper));
}
}
Then, it still doesn't work because the property is LAZY initialized and it cannot be fetched outside a transaction (and because I wrote spring.jpa.open-in-view=false in application.properties due to that being a better practice). So, I must specify explicit join using query and also must use aliases so Spring Data recognizes the properties:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface ParentRepository extends CrudRepository<Parent, Long> {
#Query("select " +
" c.parent.parentId as parentId, " +
" c.childId as childId" +
"from Child c inner join a.parent p " +
"where p.parentId=:id")
IdAndChildrenIds findIdAndChildrenIdsById(#Param("id") long id);
}
But this again doesn't work javax.persistence.NonUniqueResultException: result returns more than one elements because the specified select gives a list of tuples: List<{parentId, childId}>, while I want one tuple of {parentId, List<childId>}.
So, regarding this answer, I added #Value("#{target.parentId}") to Long getParentId();. But that did not have any effect in my case. I still get NonUniqueResultException.
Then, I tried changing the return value of the method from IdAndChildrenIds to IdAndChildrenIds just to see if the error goes away, even though that solution would not help. But that didn't work either:
Could not write JSON: No serializer found for class org.springframework.aop.framework.DefaultAdvisorChainFactory and no properties discovered to create BeanSerializer
As I said, field visibility is already set to ANY.
Versions:
- Spring Boot 1.5.9.RELEASE
- Spring Boot Starter Data JPA
- Spring Boot Starter Web
- Spring HATEOAS
Looking at this now, weird that I want parent id and ids of its children while knowing the parent id already.
interface ChildRepo{
#org.spring...Query(value = "select id from children where parent_id = :parentId", nativeQuery = true)
List<Long> findIdsByParentId(Long parentId);
}
#lombok.Value
class IdsDto{
Long parentId;
List<Long> childrenIds;
}
public IdsDto createTupleThing(Long parentId){
return new IdsDto(parentId, childRepo.findIdsByParentId(parentId);
}
I have an Entity called Person which has name property as String and person petnames has collection. When mapping to Hibernate I am getting exception. How can I resolve this problem?
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.myapp.struts;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
/**
*
* #author hyva
*/
#Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPetname(Set<String> petname) {
this.petname = petname;
}
public void setId(Long id) {
this.id = id;
}
private String name;
private Set<String> petname;
#ElementCollection
#CollectionTable(name = "petname", joinColumns =
#JoinColumn(name = "PERSON_ID"))
public Set<String> getPetname() {
return petname;
}
}
*Exception:*
Initial SessionFactory creation failed.org.hibernate.MappingException: Could not determine type for: java.util.Set, for columns: [org.hibernate.mapping.Column(petname)]
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.myapp.struts.HibernateUtil.<clinit>(HibernateUtil.java:28)
at com.myapp.struts.HibernateComplexValue.main(HibernateComplexValue.java:19)
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, for columns: [org.hibernate.mapping.Column(petname)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:266)
aJava Result: 1
Move #CollectionTable over to be an annotation on getPetname as shown below:
private Set<String> petname;
#ElementCollection
#CollectionTable(name = "pets", joinColumns =#JoinColumn(name = "PERSON_ID"))
public Set<String> getPetname(){
return petname;
}
Your annotation #CollectionTable is located after your property petName! Is is a copy/paste error?
You have not mapped your petname column. And your default property name is the same as your table name (petname)...
What is supposed to be the name of the column holding the pet names in this table?
Try adding #Column(name="YOUR_COLUMN_NAME") to force the mapping to this column.
I'm using a GlassFish 4.0 server and server-sided JPA-based classes, which I want to deliver via JAX-RS. This works fine so far for simple entities. However, if I have a #OneToMany relation for example AND there is a linked entity, the server returns a 500 internal server error. In that case, nothing is logged to the server log. In order to find the error, I created a small custom JSP page to get more info about what happened. The code is just this:
Status: <%= pageContext.getErrorData().getStatusCode() %>
Throwable: <%= pageContext.getErrorData().getThrowable() %>
Unfortunately, the output is just "Status: 500 Throwable: null"
My own server-sided code seems to run properly (did some debug output), but however, some error emerges. In this example, the User and Issue classes can be retrieved without a problem unless there is a linked IssueComment entity:
User class:
package my.application.model;
import static javax.persistence.FetchType.LAZY;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
/**
* The persistent class for the User database table.
*
*/
#XmlRootElement
#Entity(name="User")
#Table(name="User")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="failedLogin")
private short failedLogin;
#Column(name="firstname")
private String firstname;
#Column(name="lastname")
private String lastname;
#Column(name="middlename")
private String middlename;
#Column(name="password")
private String password;
#Column(name="username")
private String username;
//bi-directional many-to-one association to IssueComment
#OneToMany(mappedBy="user", fetch = LAZY)
private List<IssueComment> issueComments;
//bi-directional many-to-one association to SignalComment
#OneToMany(mappedBy="user", fetch = LAZY)
private List<SignalComment> signalComments;
//bi-directional many-to-one association to SignalMeasure
#OneToMany(mappedBy="user", fetch = LAZY)
private List<SignalMeasure> signalMeasures;
public User() {
}
public int getId() {
return this.id;
}
// more getters and setters auto-generated by Eclipse
}
User class:
package my.application.model;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;
#NamedQuery(
name = "getSingleIssue",
query = "SELECT i FROM Issue i WHERE i.id = :id"
)
/**
* The persistent class for the Issue database table.
*
*/
#XmlRootElement
#Entity(name="Issue")
#Table(name="Issue")
public class Issue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="concernedModule")
private String concernedModule;
#Column(name="createdate")
#Temporal(TemporalType.TIMESTAMP)
private Date createdate;
#Column(name="duedate")
#Temporal(TemporalType.TIMESTAMP)
private Date duedate;
#Column(name="priority")
private int priority;
#Column(name="reminderdate")
#Temporal(TemporalType.TIMESTAMP)
private Date reminderdate;
#Column(name="responsibleUserId")
private int responsibleUserId;
#Column(name="sendingModule")
private String sendingModule;
#Column(name="severity")
private int severity;
#Column(name="status")
private int status;
#Column(name="title")
private String title;
// bidirectional many-to-one association to IssueComment
#OneToMany(mappedBy = "issue")
private List<IssueComment> issueComments;
public Issue() {
}
public int getId() {
return this.id;
}
// more getters and setters....
}
IssueComment:
package my.application.model;
import java.io.Serializable;
import javax.persistence.*;
import java.util.Date;
/**
* The persistent class for the IssueComment database table.
*
*/
#Entity(name="IssueComment")
#Table(name="IssueComment")
public class IssueComment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
private int id;
#Lob
#Column(name="comment")
private String comment;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="time")
private Date time;
//bi-directional many-to-one association to Issue
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="issueId")
private Issue issue;
//bi-directional many-to-one association to User
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="userId")
private User user;
public IssueComment() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
// getters/setters....
}
The Webservice is as follows:
package my.application.server.webservice;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.server.ResourceConfig;
import my.application.data.UserStorage;
import my.application.logger.Logger;
import my.application.model.Signal;
import my.application.model.SignalComment;
import my.application.model.User;
#Provider
#Path("User")
public class UserService extends ResourceConfig {
private UserStorage storage = new UserStorage();
public UserService() {
this.packages("my.application.model");
}
#Produces(MediaType.APPLICATION_XML)
#Path("load")
#GET
public User getUser(#QueryParam("id") int id) {
try {
Logger.getInstance().log("fetching id: " + id);
User u = storage.getUser(id);
Logger.getInstance().log("number of signal comments: " + u.getSignalComments().size());
SignalComment sc = u.getSignalComments().get(0);
Logger.getInstance().log("Signal 0 comment: " + sc.getComment());
Signal s = sc.getSignal();
Logger.getInstance().log("Signal subject: " + s.getSubject());
return u;
} catch (Exception e) {
e.printStackTrace();
}
// this code is not being reached (so no errors in this method):
Logger.getInstance().log("---EXCEPTION HAS BEEN THROWN---");
return null;
}
}
I left away the client source code since it's server-sided and can be reproduced with a normal browser, so no necessity for client code here IMHO.
Make sure you don't have any cyclic references in graph (objects) you're trying to marshall to XML. For example, this could cause a problem:
User -> IssueComment -> (the same) User
or
User -> IssueComment -> Issue -> IssueComment -> (the same) User
Such structures cannot be marshalled into XML.
Note: Add #XmlRootElement annotation to IssueComment (I think it's not needed but it's better to have it there).
Note: We know about the logging issue and it will be solved as a part of JERSEY-2000.