Custom serializer java.lang.StackOverflowError: null - java

I put jsonignore all over my entity account, and a custom serializer on the Friend field.
now that I want to recover an account via controller no more worries, however as soon as I try to recover it in service with the id or other I have the following error:
java.lang.StackOverflowError: null
Entity:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int account_id;
private String firstname;
private String lastname;
#Column(unique = true)
private String username;
private boolean active;
private String avatar;
#JsonIgnore
#ManyToMany
#JoinTable(name = "ludo",
joinColumns = #JoinColumn(name = "id_account"),
inverseJoinColumns = #JoinColumn(name = "id_boardGame"))
private List<Boardgame> boardgameList;
#JsonIgnore
#OneToMany(mappedBy = "account")
List<Friends> FriendsList;
#ManyToOne
#JoinColumn(name="FK_country_Id",nullable = true,referencedColumnName = "country_id")
private Country country;
#ManyToOne
#JoinColumn(name="FK_language_Id",nullable = true,referencedColumnName = "language_id")
private Language language;
#Column(updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
}
Serializer:
public class FriendsSerializer extends StdSerializer<Friends> {
public FriendsSerializer() {
this(null);
}
public FriendsSerializer(Class<Friends> t) {
super(t);
}
#Override
public void serialize(Friends friends, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("account_id", friends.getFriend().getAccount_id());
jsonGenerator.writeStringField("username", friends.getFriend().getUsername());
jsonGenerator.writeStringField("createdAt", String.valueOf(friends.getCreatedAt()));
jsonGenerator.writeBooleanField("active", friends.isActive());
jsonGenerator.writeStringField("avatar",friends.getFriend().getAvatar());
jsonGenerator.writeFieldName("country");
jsonGenerator.writeObject(friends.getFriend().getCountry());
jsonGenerator.writeEndObject();
}
}
AccountService:
#Override
public ApiResponse getFriendNoConfirmation(Account account) {
if(account != null && account.getAccount_id() != 0){
// List<Friends> listIdAccount = this.friendRepository.getFriendsNoConfirmation(account.getAccount_id());
List<Integer> listIdAccount = this.friendRepository.getFriendsNoConfirmation(account.getAccount_id());
if(listIdAccount != null){
List<Account> listAccount = new ArrayList<>();
for(int idAccount: listIdAccount){
System.out.println(idAccount);
Account friendAccount = this.getAcountById(idAccount);
System.out.println(friendAccount);
listAccount.add(friendAccount);
}
return new ApiResponse(true, listIdAccount,BASE_CODE + "friendListNoConfirm.success");
}else{
return new ApiResponse(true, null,BASE_CODE + "friendListNoConfirm.error");
}
}else{
return new ApiResponse(true, null,BASE_CODE + "friendListNoConfirm.error");
}
}
#Override
public Account getAcountById(Integer id){
return this.accountRepository.getAccountById(id);
}

I removed the mapped by and to get my objects I make queries by joining the tables

Related

Referenced entities in Hibernate are null after parent objects are persisted

I have 2 entity classes like below. After the parent object is persisted,
accessing the foreign key referenced object returns null.The entity classes are as below.
I have an Employee entity class like below
#Entity
#Table(name = "employees")
public class Employee {
private long employeeId;
private String name;
private long companyId;
private Company companyByCompanyId;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
public long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(long employeeId) {
this.employeeId = employeeId;
}
#Basic
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "company_id")
public long getCompanyId() {
return companyId;
}
public void setCompanyId(long companyId) {
this.companyId = companyId;
}
#ManyToOne
#JoinColumn(name = "company_id", referencedColumnName = "company_id", nullable = false, insertable = false, updatable = false)
public Company getCompanyByCompanyId() {
return companyByCompanyId;
}
public void setCompanyByCompanyId(Company companyByCompanyId) {
this.companyByCompanyId = companyByCompanyId;
}
}
and a Company entity class like below
#Entity
#Table(name = "companies")
public class Company {
private long companyId;
private String companyName;
private Collection<Employee> employeesByCompanyId;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "company_id")
public long getCompanyId() {
return companyId;
}
public void setCompanyId(long companyId) {
this.companyId;
}
#Basic
#Column(name = "company_name")
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
#OneToMany(mappedBy = "companyByCompanyId")
public Collection<Employee> getEmployeesByCompanyId() {
return employeesByCompanyId;
}
public void setEmployeesByCompanyId(Collection<Employee> employeesByCompanyId) {
this.employeesByCompanyId = employeesByCompanyId;
}
}
This is the controller method in a controller class
#ApiOperation(value="Create a new employee")
#ApiResponses({})
#RequestMapping(value= "/createEmployee",method= RequestMethod.POST)
public Callable<EmployeeDTO> createEmployee(
HttpServletRequest request,
#RequestBody #Valid final List<EmployeeDTO> employeeDto) {
long employeeId = employeeService.createEmployee(employeeDto);
EmployeeDTO createdEmployee = employeeService.getEmployee(employeeId);
return () -> createdEmployee;
}
This is the method for createEmployee(employeeDto) in the service class
#Transactional
public long createEmployee(employeeDto){
Employee employee = new Employee();
employee.setName(employeeDto.getName());
employee.setCompanyId(employeeDto.getCompanyId());
Employee savedEmployee = employeeRepository.save(employee);
return savedEmployee.getEmployeeId();
}
This is the getEmployee method in the same service class as above.
public EmployeeDTO getEmployee(long employeeId) {
Employee employee = employeeRepository.findByEmployeeId(employeeId);
EmployeeDTO employeeDTO = new EmployeeDTO();
employeeDTO.setEmployeeId(employee.getEmployeeId());
employeeDTO.setName(employee.getName());
employeeDTO.setCompanyByCompanyId(employee.getCompanyByCompanyId());
}
employee.getCompanyByCompanyId() -> this is where it throws the Null pointer exception.
The companies table already has the company row that I am associating with employee.

Java Hibernate Duplicate entry error on merge

I am using Java 8 with Spring 4.3.1.RELEASE and Hibernate 5.2.1.Final and JPA with MySQL.
I have a row in the database:
PERSON TABLE
# ID, UID, JOINING_DATE, LASTACCESS_DATE, DISPLAY_NAME, EMAIL_ADDRESS, AVATAR, PROVIDER, AVATAR_FIREBASE, MILES_KM, NOTIFICATIONS, CONTACTABLE, DEVICE_TOKEN
'384', 'h6qQg5YfQveTaCyBEXwDMSJPqwk1', '1499148701258', '1499240111170', 'Richard', 'richardmarais#gmail.com', ?, '3', 'https://scontent.xx.fbcdn.net/v/t1.0-1/p100x100/14484731_10155363658503146_8505143722410369457_n.jpg?oh=3b6fbb0facc9457f7e8387e4853278a8&oe=59D6034D', '0', '1', NULL, 'e_6U3WAVHu0:APA91bFibmAaVTLO13vG1Aww0yLK2UzRNopLK1UalurfYRXcCbUDrTJQKOm0hiKkyxV1auEWTL9od5ek62FfJIzo1li6vrgA6CfxE6Cu2HnPkSPGaeFispI2c16UBZcLZfYGRe1i9nmf'
When I try use Hibernate to merge an object with the existing row, it throws an error that it's a duplicate. I thought because I am doing a merge, it would UPDATE the existing row.
I have the following code:
PersonServiceImpl.java
#Transactional
public class PersonServiceImpl implements PersonService {
#Override
public Person save(Person person) throws Exception {
try {
person = personDao.merge(person);
which calls:
PersonDaoImpl.java
public class PersonDaoImpl extends JpaDao<Long, Person> implements PersonDao
#Override
public Person merge(Person person) throws InstantiationException, IllegalAccessException {
Person foundPerson = null;
if (!StringUtils.isEmpty(person.getUid())) {
foundPerson = findByUid(person.getUid(), person.getProvider());
}
if (foundPerson == null && !StringUtils.isEmpty(person.getId())) {
foundPerson = findById(person.getId());
}
if ((foundPerson == null || foundPerson.getUid() == null) && person.getEmailAddress() != null) {
foundPerson = findByEmail(person.getEmailAddress());
if (foundPerson != null) {
foundPerson.setUid(person.getUid());
}
}
if (foundPerson != null && person.getUid().equals(foundPerson.getUid())) {
if (person.getLocations().isEmpty() && foundPerson.getLocations() != null && !foundPerson.getLocations().isEmpty()) {
person.setLocations(foundPerson.getLocations());
}
BeanUtils.copyProperties(person, foundPerson);
if (foundPerson.getLocations().isEmpty() && person.getLocations() != null && !person.getLocations().isEmpty()) {
foundPerson.setLocations(person.getLocations());
}
foundPerson.setLastAccessDate(System.currentTimeMillis());
if (Objects.isNull(foundPerson.getJoiningDate()) || foundPerson.getJoiningDate() == 0) {
foundPerson.setJoiningDate(System.currentTimeMillis());
}
return super.merge(foundPerson); // << line executed
} else {
return super.merge(person);
}
}
which calls:
JpaDao.java
public class JpaDao<I, T extends AbstractDomain<I>> {
protected Class<T> entityClass;
protected T merge(T entity) throws InstantiationException, IllegalAccessException {
T attached = null;
if (entity.getId() != null) {
attached = entityManager.find(entityClass, entity.getId()); // << line executed
}
if (attached == null) {
attached = entityClass.newInstance();
}
BeanUtils.copyProperties(entity, attached);
entityManager.setFlushMode(FlushModeType.COMMIT);
attached = entityManager.merge(attached);
return attached;
}
However, when I try merge the Person object, I get the following error:
Error
09:53:10,775 WARN [org.hibernate.engine.jdbc.spi.SqlExceptionHelper]
(default task-33) SQL Error: 1062, SQLState: 23000 09:53:10,781 ERROR
[org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (default task-33)
Duplicate entry '384-8576' for key 'PRIMARY' 09:53:10,781 INFO
[org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl] (default
task-33) HHH000010: On release of batch it still contained JDBC
statements 09:53:10,782 ERROR
[org.hibernate.internal.ExceptionMapperStandardImpl] (default task-33)
HHH000346: Error during managed flush
[org.hibernate.exception.ConstraintViolationException: could not
execute statement]
Question
How would I implement the code in order to allow UPDATES if the row exists?
Thank you
More info
Here is the Entity concerned:
Person.java
#Entity
#Table(name = "person")
#XmlRootElement(name = "person")
public class Person extends AbstractDomain<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Size(min = 1, max = 45)
#Column(name = "UID")
private String uid;
#Column(name = "JOINING_DATE", nullable = false)
private Long joiningDate;
#Column(name = "LASTACCESS_DATE", nullable = false)
private Long lastAccessDate;
#Size(min = 1, max = 85)
#Column(name = "DISPLAY_NAME", nullable = false)
private String displayName;
#Size(min = 5, max = 55)
#Column(name = "EMAIL_ADDRESS", nullable = false, unique = true)
private String emailAddress;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "person_language", joinColumns = {
#JoinColumn(name = "PER_ID", referencedColumnName = "ID") }, inverseJoinColumns = {
#JoinColumn(name = "LANG_ID", referencedColumnName = "LANGUAGE_CODE", unique = true) })
private Set<Language> languages;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinTable(name = "person_location", joinColumns = {
#JoinColumn(name = "PER_ID", referencedColumnName = "ID") }, inverseJoinColumns = {
#JoinColumn(name = "LOC_ID", referencedColumnName = "ID", unique = true) })
private Set<LocationPerson> locations = null;
#Basic(fetch = FetchType.LAZY)
#Lob
#Column(name = "AVATAR", nullable = true)
private byte[] avatar;
#Column(name = "PROVIDER")
private Integer provider;
#Column(name = "AVATAR_FIREBASE")
private String avatarFirebase;
#Column(name = "MILES_KM")
private Integer milesKm;
#Column(name = "NOTIFICATIONS")
private Integer notifications;
#Size(max = 256)
#Column(name = "DEVICE_TOKEN")
private String deviceToken;
#XmlElement
public String getDeviceToken() {
return deviceToken;
}
public void setDeviceToken(String deviceToken) {
this.deviceToken = deviceToken;
}
#XmlElement
public Integer getMilesKm() {
return milesKm;
}
public void setMilesKm(Integer milesKm) {
this.milesKm = milesKm;
}
#Column(name = "CONTACTABLE")
private Long contactable;
#Transient
private Boolean contactableFree;
#XmlElement
public Boolean getContactableFree() {
return contactableFree;
}
public void setContactableFree(Boolean contactableFree) {
this.contactableFree = contactableFree;
}
#XmlElement
public Long getContactable() {
return contactable;
}
public void setContactable(Long contactable) {
this.contactable = contactable;
}
#XmlElement
public Integer getNotifications() {
return notifications;
}
public void setNotifications(Integer notifications) {
this.notifications = notifications;
}
#XmlElement
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#XmlElement
public Long getJoiningDate() {
return joiningDate;
}
public void setJoiningDate(Long joiningDate) {
this.joiningDate = joiningDate;
}
#XmlElement
public Long getLastAccessDate() {
return lastAccessDate;
}
public void setLastAccessDate(Long lastAccessDate) {
this.lastAccessDate = lastAccessDate;
}
#XmlElement
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
#XmlElement
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
#XmlElement
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
#XmlElement
public Set<Language> getLanguages() {
return languages;
}
public void setLanguages(Set<Language> languages) {
this.languages = languages;
}
#XmlElement
public Set<LocationPerson> getLocations() {
return locations;
}
public void setLocations(Set<LocationPerson> locations) {
if (this.locations == null) {
this.locations = locations;
} else {
this.locations.retainAll(locations);
this.locations.addAll(locations);
}
}
public void putLocations(Set<LocationPerson> locations) {
if (locations != null) {
this.locations.clear();
this.locations.addAll(locations);
} else {
this.locations = locations;
}
}
#XmlElement
public byte[] getAvatar() {
return avatar;
}
public void setAvatar(byte[] avatar) {
this.avatar = avatar;
}
#XmlElement
public Integer getProvider() {
return provider;
}
public void setProvider(Integer provider) {
this.provider = provider;
}
#XmlElement
public String getAvatarFirebase() {
return avatarFirebase;
}
public void setAvatarFirebase(String avatarFirebase) {
this.avatarFirebase = avatarFirebase;
}
}
UPDATE
After reading the following, I change the Person object locations from CascadeType.ALL to CascadeType.PERSIST.
Person.java
#OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinTable(name = "person_location", joinColumns = {
#JoinColumn(name = "PER_ID", referencedColumnName = "ID") }, inverseJoinColumns = {
#JoinColumn(name = "LOC_ID", referencedColumnName = "ID", unique = true) })
private Set<LocationPerson> locations = null;
The update is done to the database, but I now get the following:
10:41:20,657 ERROR
[org.hibernate.internal.ExceptionMapperStandardImpl] (default task-7)
HHH000346: Error during managed flush
[org.hibernate.TransientObjectException: object references an unsaved
transient instance - save the transient instance beforeQuery flushing:
com.jobs.spring.domain.LocationPerson] 10:41:21,429 INFO
[org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl] (default
task-7) HHH000010: On release of batch it still contained JDBC
statements

JsonMappingException with Arrays of object in spring-jpa

i get an error when i try to get an item from my dbms. following error
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.pharmawizardcabinet.core.entity.cabinet.Cabinet.listaFarmaci, could not initialize proxy - no Session (through reference chain: com.pharmawizardcabinet.web.beans.ResponseCabinet["cabinet"]->com.pharmawizardcabinet.core.entity.cabinet.Cabinet["listaFarmaci"])
this is my conteiner
#Entity
#Table(name = "Cabinet")
public class Cabinet implements Serializable {
private static final long serialVersionUID = 7311927404447970875L;
#Id
#Column(name = "Id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "cabinet")
private List<Farmaco> listaFarmaci;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "user")
private User user;
#Column(name = "timestamp")
#Temporal(TemporalType.DATE)
private Date setLastModified;
public Cabinet() {
}
#PostPersist
#PostUpdate
private void setLastUpdate() {
this.setLastModified = new Date();
}
public List<Farmaco> getListaFarmaci() {
return listaFarmaci;
}
public void setListaFarmaci(List<Farmaco> listaFarmaci) {
this.listaFarmaci = listaFarmaci;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public Date getSetLastModified() {
return setLastModified;
}
public void setSetLastModified(Date setLastModified) {
this.setLastModified = setLastModified;
}
}
and this is the item
#Entity
#Table(name = "Farmaco")
public class Farmaco implements Serializable {
private static final long serialVersionUID = -152536676742398255L;
public Farmaco() {
// TODO Auto-generated constructor stub
}
#Column(name = "nome_farmaco")
private String nome;
#Column(name = "codice")
private String codice;
#Column(name = "azienda")
private String azienda;
#Id
#Column(name = "Id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
#Column(name = "scadenza")
#Temporal(TemporalType.DATE)
private Date scadenza;
#Enumerated(EnumType.STRING)
#Column(name = "posologia")
private Posologia posologia;
#Column(name = "quantita")
private Integer quantita;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "note")
private Note note;
#ManyToOne(cascade =CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "cabinet_id")
private Cabinet cabinet;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCodice() {
return codice;
}
public void setCodice(String codice) {
this.codice = codice;
}
public String getAzienda() {
return azienda;
}
public void setAzienda(String azienda) {
this.azienda = azienda;
}
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public Date getScadenza() {
return scadenza;
}
public void setScadenza(Date scadenza) {
this.scadenza = scadenza;
}
public Posologia getPosologia() {
return posologia;
}
public void setPosologia(Posologia posologia) {
this.posologia = posologia;
}
public Integer getQuantita() {
return quantita;
}
public void setQuantita(Integer quantita) {
this.quantita = quantita;
}
public Note getNote() {
return note;
}
public void setNote(Note note) {
this.note = note;
}
public Cabinet getCabinet() {
return cabinet;
}
public void setCabinet(Cabinet cabinet) {
this.cabinet = cabinet;
}
}
controller is this
#Component("managerCabinet")
public class ManagerCabinet {
private static Logger logger = Logger.getLogger(ManagerCabinet.class);
#PersistenceContext(name = "pwcabinet-jpa")
private EntityManager entityManager;
#Transactional
public Cabinet getCabinetByUser(User user) {
logger.debug("[getCabinetByUser] user: " + user.getId());
return _getCabinetByUser(user);
}
private Cabinet _getCabinetByUser(User user) {
logger.debug("[_getCabinetByUser] user: " + user.getId());
User find = entityManager.find(User.class, user.getId());
Query searchCabinetByUser = entityManager.createQuery("Select c from Cabinet c where c.user = :userId", Cabinet.class);
searchCabinetByUser.setParameter("userId", find);
Cabinet cabinetSearch = (Cabinet) searchCabinetByUser.getSingleResult();
cabinetSearch.setUser(find);
return cabinetSearch;
}
}
but i continue to get error.
if i use the annotation #JsonIgnore in this way
#JsonIgnore
public List<Farmaco> getListaFarmaci() {
return listaFarmaci;
}
they works, but i need this information in my result. how i solve it?
When your method private Cabinet _getCabinetByUser(User user) returns the Cabinet instance is then in the 'detached' state, viz. is no longer associated with a persistence context.
When an item is in a detached state non-eagerly fetched associations can longer be accessed.
As the default fetch for #OneToMany is Lazy then in your case
#OneToMany(cascade = CascadeType.ALL, mappedBy = "cabinet")
private List<Farmaco> listaFarmaci;
the field listaFarmaci can no longer be accessed once the loaded Cabinet is detached from the persistence context.
You have various means of dealing with this which would include:
Marking the field as being eagerly fetched (not good as will always be eagerly fetched regardless of whether required or not).
Forcing the persistence context to remain open until all processing is done typically referred to as the OpenSessionInView pattern (or anti-pattern) depending on your point of view: http://java.dzone.com/articles/open-session-view-design
Ensuring all data required for use case is initialized before detachment. There are various ways of achieving this:
Simply accessing the collection is some way e.g. by calling size() but this may not work with all JPA providers.
Specifying FETCH JOIN in your JPQL query which loads the Cabinet (although this has side effects). http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Join_Fetching

JPA - Get collection from ManyToOne relationship

I have a table Post and Post_Image
#Entity
#Table(name = "post")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Post.findAll", query = "SELECT p FROM Post p"),
#NamedQuery(name = "Post.findByPostId", query = "SELECT p FROM Post p WHERE p.postId = :postId"),
#NamedQuery(name = "Post.findByTitle", query = "SELECT p FROM Post p WHERE p.title = :title"),
#NamedQuery(name = "Post.findByCreatedDatetime", query = "SELECT p FROM Post p WHERE p.createdDatetime = :createdDatetime")})
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#NotNull
#Column(name = "post_id")
private Integer postId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 500)
#Column(name = "title")
private String title;
#Basic(optional = false)
#NotNull
#Lob
#Size(min = 1, max = 65535)
#Column(name = "content")
private String content;
#Column(name = "created_datetime")
#Temporal(TemporalType.TIMESTAMP)
private Date createdDatetime;
#JoinColumn(name = "user_id", referencedColumnName = "user_id")
#ManyToOne(optional = false)
private User userId;
#JoinColumn(name = "post_type_id", referencedColumnName = "post_type_id")
#ManyToOne(optional = false)
private PostType postTypeId;
public Post() {
Date date = new Date();
this.createdDatetime =new Date(date.getTime());
}
public Post(Integer postId) {
this.postId = postId;
}
public Post(Integer postId, String title, String content) {
this.postId = postId;
this.title = title;
this.content = content;
}
public Integer getPostId() {
return postId;
}
public void setPostId(Integer postId) {
this.postId = postId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getCreatedDatetime() {
return createdDatetime;
}
public void setCreatedDatetime(Date createdDatetime) {
this.createdDatetime = createdDatetime;
}
public User getUserId() {
return userId;
}
public void setUserId(User userId) {
this.userId = userId;
}
public PostType getPostTypeId() {
return postTypeId;
}
public void setPostTypeId(PostType postTypeId) {
this.postTypeId = postTypeId;
}
#Override
public int hashCode() {
int hash = 0;
hash += (postId != null ? postId.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 Post)) {
return false;
}
Post other = (Post) object;
if ((this.postId == null && other.postId != null) || (this.postId != null && !this.postId.equals(other.postId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.Post[ postId=" + postId + " ]";
}
}
and
#Entity
#Table(name = "post_image")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "PostImage.findAll", query = "SELECT p FROM PostImage p"),
#NamedQuery(name = "PostImage.findByPostImageId", query = "SELECT p FROM PostImage p WHERE p.postImageId = :postImageId"),
#NamedQuery(name = "PostImage.findByPath", query = "SELECT p FROM PostImage p WHERE p.path = :path"),
#NamedQuery(name = "PostImage.findByTitle", query = "SELECT p FROM PostImage p WHERE p.title = :title")})
public class PostImage implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "post_image_id")
private Integer postImageId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 500)
#Column(name = "path")
private String path;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 500)
#Column(name = "title")
private String title;
#JoinColumn(name = "post_id", referencedColumnName = "post_id")
#ManyToOne(optional = false)
private Post postId;
public PostImage() {
}
public PostImage(Integer postImageId) {
this.postImageId = postImageId;
}
public PostImage(Integer postImageId, String path, String title) {
this.postImageId = postImageId;
this.path = path;
this.title = title;
}
public Integer getPostImageId() {
return postImageId;
}
public void setPostImageId(Integer postImageId) {
this.postImageId = postImageId;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Post getPostId() {
return postId;
}
public void setPostId(Post postId) {
this.postId = postId;
}
#Override
public int hashCode() {
int hash = 0;
hash += (postImageId != null ? postImageId.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 PostImage)) {
return false;
}
PostImage other = (PostImage) object;
if ((this.postImageId == null && other.postImageId != null) || (this.postImageId != null && !this.postImageId.equals(other.postImageId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.PostImage[ postImageId=" + postImageId + " ]";
}
}
i want to get collection of images for particular post like
Collection objPostImage = objPost.getPostImageCollection()
but manytoone relationship do not provide this functionality to me how can i convert it to one to many or how can i get Image Collection for a post.?
I am new to java so any help and suggestion will be appreciated
thanx in advance...
You can add a java.util.Set of PostImages in your Post object, and use the Hibernate mapping to provide the relationship. This site has a great example of setting up One to Many relationships.
So, for example, you would want to add something like the following to your Post class:
private Set<PostImage> postImages = new HashSet<PostImage>();
#OneToMany(fetch = FetchType.LAZY, mappedBy = "post")
public Set<PostImage> getPostImages() {
return this.postImages;
}
public void setPostImages(Set<PostImage> postImages) {
this.postImages= postImages;
}
Then, in your PostImage class, add a reference to a Post object:
private Post post;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "POST_ID", nullable = false)
public Stock getPost() {
return this.post;
}
public void setPost(Post post) {
this.post= post;
}
After adding that, you will be able to call the getPostImages() method on your Post object.
Try this:
#Entity
#Table(name = "post")
public class Post
{
//....
#OneToMany(mappedBy = "post")
private Set<PostImage> images;
//....
}
#Entity
#Table(name = "post_image")
public class PostImage
{
//....
#JoinColumn(name = "post_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Post post;
//....
}
The reason why Seth's answer didn't work is because EclipseLink uses fields to access persistence data. (Hibernate uses properties IIRC.) You can specify per class how a JPA provider should access this data.
Using fields:
#Entity
#Access(AccessType.FIELD)
public class SomeEntity
{
#Id
private Long id;
//....
}
Using properties:
#Entity
#Access(AccessType.PROPERTY)
public class SomeEntity
{
private Long id;
//....
#Id
public Long getId()
{
return id;
}
}
However when using #Access(AccessType.PROPERTY) fields are also used (at least in EclipseLink) so something like this is possible:
#Entity
#Access(AccessType.PROPERTY)
public class SomeEntity
{
private Long id;
#Column(name = "text")
private String someText;
//....
#Id
public Long getId()
{
return id;
}
}

Multiple beans validation inside containing bean with different group interfaces

I have problem with validation a very specific beans.
Let me give you some code first:
#Entity
#Table(name = "customers", schema = "public", uniqueConstraints = #UniqueConstraint(columnNames = {"cus_email" }))
public class Customers extends ModelObject implements java.io.Serializable {
private static final long serialVersionUID = -3197505684643025341L;
private long cusId;
private String cusEmail;
private String cusPassword;
private Addresses shippingAddress;
private Addresses invoiceAddress;
#Id
#Column(name = "cus_id", unique = true, nullable = false)
#SequenceGenerator(name = "cus_seq", sequenceName = "customers_cus_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "cus_seq")
#NotNull
public long getCusId() {
return cusId;
}
public void setCusId(long cusId) {
this.cusId = cusId;
}
#NotEmpty
#Size(min=5, max=255)
#Email
#Column(name = "cus_email", unique = true, nullable = false, length = 255)
public String getCusEmail() {
return cusEmail;
}
public void setCusEmail(String cusEmail) {
this.cusEmail = cusEmail;
}
#NotNull
#Column(name = "cus_password", nullable = false)
public String getCusPassword() {
return cusPassword;
}
public void setCusPassword(String cusPassword) {
this.cusPassword = cusPassword;
}
#NotNull
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cus_shipping_adr_id", nullable = false)
#Cascade(value = CascadeType.ALL)
#Valid
public Addresses getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(Addresses cusShippingAddress) {
this.shippingAddress = cusShippingAddress;
}
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cus_invoice_adr_id", nullable = true)
#Cascade(value = CascadeType.ALL)
#Valid
public Addresses getInvoiceAddress() {
return invoiceAddress;
}
public void setInvoiceAddress(Addresses cusInvoiceAddress) {
this.invoiceAddress = cusInvoiceAddress;
}
}
As you can see, I have here two address fields - one for shipping address, the other for invoice address.
The validation for each type of address should be different, as e.g. I don't need VAT number in shipping address, but I may want that in invoice.
I used groups to perform different validation on invoice address and shipping address which works OK if I do manual validation of address field.
But now I'd like to validate whole Customer object with addresses (if available).
I tried to do that with code below:
private void validateCustomerData() throws CustomerValidationException {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Customers>> constraintViolations;
constraintViolations = validator.validate(customer, Default.class, InvoiceAddressCheck.class, ShippingAddressCheck.class);
if (!constraintViolations.isEmpty()) {
throw new CustomerValidationException(3, Message.CustomerDataException, constraintViolations);
}
}
Of course this doesn't work as it supposed, since both validations are run on both instances of address objects inside customer object, so I get errors in shipping address from InvoiceAddressCheck interface and errors in invoice address from ShippingAddressCheck.
Here is shortened declaration of Addresses bean:
#Entity
#Table(name = "addresses", schema = "public")
#TypeDef(name = "genderConverter", typeClass = GenderConverter.class)
public class Addresses extends ModelObject implements Serializable{
private static final long serialVersionUID = -1123044739678014182L;
private long adrId;
private String street;
private String houseNo;
private String zipCode;
private String state;
private String countryCode;
private String vatNo;
private Customers customersShipping;
private Customers customersInvoice;
public Addresses() {}
public Addresses(long adrId) {
super();
this.adrId = adrId;
}
#Id
#Column(name = "adr_id", unique = true, nullable = false)
#SequenceGenerator(name = "adr_seq", sequenceName = "adr_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "adr_seq")
#NotNull
public long getAdrId() {
return adrId;
}
public void setAdrId(long adrId) {
this.adrId = adrId;
}
#NotNull
#Column(name = "adr_street", nullable = false)
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
#NotEmpty(groups = ShippingAddressCheck.class)
#Column(name = "adr_house_no")
public String getHouseNo() {
return houseNo;
}
#NotEmpty(groups = ShippingAddressCheck.class)
#Column(name = "adr_zip_code")
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
#Column(name = "adr_vat_no")
#NotEmpty(groups = InvoiceAddressCheck.class)
public String getVatNo() {
return vatNo;
}
public void setVatNo(String vatNo) {
this.vatNo = vatNo;
}
#OneToOne(fetch = FetchType.LAZY, mappedBy = "shippingAddress")
public Customers getCustomersShipping() {
return customersShipping;
}
public void setCustomersShipping(Customers customersShipping) {
this.customersShipping = customersShipping;
}
#OneToOne(fetch = FetchType.LAZY, mappedBy = "invoiceAddress")
public Customers getCustomersInvoice() {
return customersInvoice;
}
public void setCustomersInvoice(Customers customersInvoice) {
this.customersInvoice = customersInvoice;
}
}
Is there any way to run the validation, so that invoiceAddress is validated with InvoiceAddressCheck group and shippingAddress validated with ShippingAddressCheck group, but run during validation of Customer object?
I know that I can do it manually for each subobject, but that is not the point in here.
Temp solution for now is to write custom validation for invoice field, so it checks only InvoiceAddressCheck.
Here is the code I have
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = {InvoiceAddressValidator.class })
public #interface InvoiceAddressChecker {
String message() default "Invoice address incorrect.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator:
public class InvoiceAddressValidator implements ConstraintValidator<InvoiceAddressChecker, Addresses> {
#Override
public void initialize(InvoiceAddressChecker params) {
}
#Override
public boolean isValid(Addresses invoiceAddress, ConstraintValidatorContext context) {
// invoice address is optional
if (invoiceAddress == null) {
return true;
}
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Addresses>> constraintViolations;
constraintViolations = validator.validate(invoiceAddress, Default.class, InvoiceAddressCheck.class);
if (constraintViolations.isEmpty()) {
return true;
} else {
context.disableDefaultConstraintViolation();
Iterator<ConstraintViolation<Addresses>> iter = constraintViolations.iterator();
while (iter.hasNext()) {
ConstraintViolation<Addresses> violation = iter.next();
context.buildConstraintViolationWithTemplate(violation.getMessage()).addNode(
violation.getPropertyPath().toString()).addConstraintViolation();
}
return false;
}
}
}
And model annotation:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cus_invoice_adr_id", nullable = true)
#Cascade(value = CascadeType.ALL)
#InvoiceAddressChecker
public Addresses getInvoiceAddress() {
return invoiceAddress;
}
It's not really great solution, but it does what I need.
If you figure out better solution, please let me know :)

Categories

Resources