Jsonparsing error with #JsonIgnore on Hibernate - java

I get this exception when Jackson tries to parse my data to Json:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: packagename.Thing.Stuffs, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->packagename.Stuff[“thing"]->packagename.Thing[“stuffs"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: packagename.Thing.Stuffs, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->packagename.Stuff[“thing"]->packagename.Thing[“stuffs"])
I have the following entities(names have been replaced with Stuff and Thing):
Stuff:
#Entity
#Table(name = "stuff")
#SQLDelete(sql = "UPDATE stuff SET deleted = 1 WHERE id = ?")
#Where(clause = "deleted = 0")
public class Stuff implements Serializable {
private Long id;
private Thing thing;
private String stuffName;
#Id
#Column(name = "id", unique = true, nullable = false)
#GeneratedValue
public Long getId() {
return this.id;
}
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, optional = false)
#JoinColumn(name = "thing_id", nullable = false)
public Thing getThing() {
return thing;
}
#Transient
public String getStuffName() {
return stuffName;
}
// Setters and constructor(s) omitted
}
Thing:
#Entity
#Table(name = "thing")
public class Thing implements Serializable {
private Long id;
private String name;
private List<Stuff> stuffs;
#Id
#Column(name = "id", unique = true, nullable = false)
#GeneratedValue
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name", unique = false, nullable = false, length = 45)
public String getName() {
return this.name;
}
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "thing_id")
#JsonIgnore
public List<Stuffs> getStuffs() {
return stuffs;
}
// Setters and constructor(s) omitted
}
I have been googling for this type of error. It seems that this is happening when the json-parser is trying to parse objects that aren't loaded due to lazy-load. Lazy load seems to be on by default. I'd like to avoid setting everything to eager so I put #JsonIgnore instead. Changes nothing for me. Help would be greatly appreciated, this is driving me nuts.
EDIT:
Adding #JsonIgnore and #Eager in both classes gives me another problem. An exception that is looking like this:
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException Handling of [org.springframework.http.converter.HttpMessageNotWritableException] resulted in Exception java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
Searching for that on stackoverflow gives me this: Hibernate and Jackson (java.lang.IllegalStateException: Cannot call sendError() after the response has been committed)
The link is basically saying that I should add #JsonIgnore, and I have already done that.

From comments you said that you are using Jackson 1.9.10, which will allow you to use the new syntax of #JsonProperty annotation by adding READ_ONLY and WRITE_ONLY property for access type.
So you can use :
#JsonProperty(access = Access.WRITE_ONLY)
With your field definition.
For further details you can check Only using #JsonIgnore during serialization, but not deserialization discussion.
Note:
With older cersions you could just use #JsonIgnore on class member getter and #JsonProperty on its setter.

Maybe your problem caused by wrong mapping, you shouldn't use #JoinColumn for #OneToMany relation in Thing entity, you need to add mappedBy = "thing" as parameter to specify relationship correctly.

Related

Auto generated id not cascading on EmbeddedId in child entity in Hibernate

When trying to insert an Entity - Awith a set Another Entity B, B should get the Auto generated Id from A but its null.
Tried and failed:
#MapsId("taskPKId.storyId.id") - Same error.
#Embeddable
class StoryId {
#ManyToOne(fetch = FetchType.Lazy)
#JoinColumn(name = "STORY_ID")
Long id;
} //Incomprehensible Null pointer exception
mappedBy("story") - same error
Tried with mappedBy('story') but getting an error with repeated column and so had to map it with insertable=false and updatable=false [Hibernate doesn't recognize insertable=false for #EmbeddedId]
I am getting STORY_ID = null and therefore saveAll fails on storyRepository.saveAll(stories) where storyRepository is a Spring Data repository
#Table(name = "STORY")
#EqualsAndHashCode
class Story {
#Id
#GeneratedValue(stratergy=GenerationType.Auto)
#Column(name="STORY_ID")
Long id;
#Column(name="STORY_NAME")
String name;
//#OneToMany(cascade=ALL, mappedBy="taskPKId.storyId.id", fetch = FetchType.Lazy) // tried this as well
#OneToMany(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
Set<Task> task;
}
#Table(name = "TASK_XREF")
#EqualsAndHashCode
Class Task {
#EmbeddedId
TaskPKId taskPKId;
#Column(name = "TASK_NAME")
String name;
#ManyToOne (fetch = FetchType.Lazy, optional = false)
#JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID", nullable = false, insertable = false, updatable = false)
Story story;
}
#Embeddable
#EqualsAndHashCode
Class TaskPKId implements Serializable {
TaskId taskId;
TaskTypeId taskTypeId;
StoryId storyId;
}
#Embeddable
#EqualsAndHashCode
class StoryId implements Serializable {
#Column(name = "STORY_ID")
Long id;
}
Tables:
STORY [STORY_ID, STORY_NAME]
TASK_XREF [(TASK_ID(FK), TASK_TYPE_ID(FK), STORY_ID(FK)) PK,TASK_NAME]
Story gets inserted (before commit ofcourse), but fails because STORY_ID is sent as null to TASK_XREF for the next inserts
I'm not quite sure why your configuration does not work. I have a similar configuration in one of my projects that works just fine. I was able to find a solution however, by adding a #MapsId annotation to the ManyToOne in the Task class. (see can someone please explain me #MapsId in hibernate? for an explanation about MapsId) I also removed insertable=false and updatable=false. See below for the code.
I didn't get MapsId to work with the StoryId class, so i changed the type of TaskPKID.storyId from StoryId to long. The StoryId class doesn't seem to add much, so hopefully this isn't to much of a problem. If you find a solution please let me know in the comments though!
By the way, your code has a lot of problems. There's a bunch of typo's, and there is a OneToMany mapping on a property that is not a Collection (which isn't allowed) This made it more difficult for me to debug the problem. Please make sure to post better quality code in your questions next time.
Here is the Task class the way I implemented it:
#Entity
#Table(name = "TASK_XREF")
class Task {
#EmbeddedId
TaskPKId taskPKId;
#Column(name = "TASK_NAME")
String name;
#MapsId("storyId")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "STORY_ID")
Story story;
//getters, setters
}
And here is the TaskPKID class:
#Embeddable
class TaskPKId implements Serializable {
long taskId;
long taskTypeId;
#Column(name="STORY_ID")
long storyId;
public long getTaskId() {
return taskId;
}
public void setTaskId(long taskId) {
this.taskId = taskId;
}
public void setTaskTypeId(long taskTypeId) {
this.taskTypeId = taskTypeId;
}
}
I'm not sure what you want to achieve, but it looks like you have combined #OneToMany annotation with #OneToOne-like implementation (which can lead to unexpected behavior like this one).
Possible solutions:
If one story owns multiple tasks
// Story.java
#OneToMany(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
Set<Task> task; // basically Set, List or any other collection type
// Task.java
#ManyToOne (fetch = FetchType.Lazy, optional = false)
#JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID")
Story story;
If one story owns only one task
// Story.java
#OneToOne(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
Task task;
// Task.java
#OneToOne (fetch = FetchType.Lazy, optional = false)
#JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID")
Story story;
Further reading:
#OneToOne
#OneToMany

Problem with #ManyToOne map in EclipseLink

I'm having trouble with this #ManyToOne map, searched a lot, but still can't find a solution for this problem.
I have these two classes, i will never insert anything into TB_MANUAL, i'll just use it as reference for the CD_MANUAL field in TB_COMPANY, like this:
Company company = new Company();
company.setManual("2"); //Theres already a row with this id in the TB_MANUAL
and then persist company, but i got this error:
Caused By: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: 2.
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.discoverUnregisteredNewObjects(RepeatableWriteUnitOfWork.java:313)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.java:723)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1516)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3168)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:355)
Truncated. see log file for complete stacktrace
-
#Entity
#Table(name = "TB_COMPANY", schema = "ADMPROD")
#Cacheable
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
public Company() {}
public Company(String id) {
this.id = id;
}
#ManyToOne
#JoinColumn(name = "CD_MANUAL", referencedColumnName = "CD_MANUAL", nullable
= true)
private Manual manual;
public void setManual(String idManual) {
this.manual = new Manual(idManual);
}
}
and
#Entity
#Table(name = "TB_MANUAL")
public class Manual implements Serializable{
private static final long serialVersionUID = 1L;
public Manual() {
}
public Manual(String id) {
this.id = id;
}
#Id
#Column(name = "CD_MANUAL")
private String id;
#Column(name = "DS_OBS_MANUAL")
private String description;
}
You create new Manual every time you set it, so your object is detach from EntityManager, or has not data at all.
I don't argue if that is a good design (althought I've never would do it like that), to over come your problem you should add CascadeType.PERSIST to your relation.
#ManyToOne
#JoinColumn(name = "CD_MANUAL", referencedColumnName = "CD_MANUAL", nullable
= true, cascade = CascadeType.PERSIST)
private Manual manual;
The problem was in the Manual table primary key, the JPA doesnt find any row with id 1 because the primary key of Manual is char(2), passing "1 " instead of "1" solved the problem.

JPA: How to handle versioned entities?

I have a versioning on an entity as part of its primary key. The versioning is done via a timestamp of the last modification:
#Entity
#Table(name = "USERS")
#IdClass(CompositeKey.class)
public class User {
#Column(nullable = false)
private String name;
#Id
#Column(name = "ID", nullable = false)
private UUID id;
#Id
#Column(name = "LAST_MODIFIED", nullable = false)
private LocalDateTime lastModified;
// Constructors, Getters, Setters, ...
}
/**
* This class is needed for using the composite key.
*/
public class CompositeKey {
private UUID id;
private LocalDateTime lastModified;
}
The UUID is translated automatically into a String for the database and back for the model. The same goes for the LocalDateTime. It gets automatically translated to a Timestamp and back.
A key requirement of my application is: The data may never update or be deleted, therefore any update will result in a new entry with a younger lastModified. This requirement is satisfied with the above code and works fine until this point.
Now comes the problematic part: I want another object to reference on a User. Due to versioning, that would include the lastModified field, because it is part of the primary key. This yields a problem, because the reference might obsolete pretty fast.
A way to go might be depending on the id of the User. But if I try this, JPA tells me, that I like to access a field, which is not an Entity:
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#Column(nullable = false)
private UUID id;
#OneToOne(optional = false)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private UUID userId;
#Column(nullable = false)
private boolean married;
// Constructors, Getter, Setter, ...
}
What would be the proper way of solving my dilemma?
Edit
I got a suggestion by JimmyB which I tried and failed too. I added the failing code here:
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#Column(nullable = false)
private UUID id;
#OneToMany
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private List<User> users;
#Column(nullable = false)
private boolean married;
public User getUser() {
return users.stream().reduce((a, b) -> {
if (a.getLastModified().isAfter(b.getLastModified())) {
return a;
}
return b;
}).orElseThrow(() -> new IllegalStateException("User detail is detached from a User."));
}
// Constructors, Getter, Setter, ...
}
What you seem to require seems to be on the lines of a history table, to keep track of the changes. See https://wiki.eclipse.org/EclipseLink/Examples/JPA/History on how EclipseLink can handle this for you while using normal/traditional JPA mappings and usage.
What you have here is a logical 1:1 relationship which, due to versioning, becomes a technical 1:n relationship.
You have basically three options:
Clean JPA way: Declare an 'inverse' #ManyToOne relationship from user to the "other object" and make sure you always handle it whenever a new User record is created.
'Hack-ish' way: Declare a #OneToMany relationship in the "other object" and force it to use a specific set of columns for the join using #JoinColumn. The problem with this is that JPA always expects unique reference over the join columns so that reading the UserDetail plus referenced User records should work, whereas writing UserDetail should not cascade onto User to avoid unwanted/undocumented effects.
Just store the user's UUID in the "other object" and resolve the reference yourself whenever you need it.
The added code in your question is wrong:
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private UUID userId;
More correct, albeit not with the result you want, would be
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private User user;
This won't work though, because, as I said above, you may have more than one user record per UserDetail, so you'd need a #OneToMany relationship here, represented by a Collection<User>.
Another 'clean' solution is to introduce an artificial entity with a 1:1 cardinality w.r.t. to the logical User to which you can refer, like
#Entity
public class UserId {
#Id
private UUID id;
#OneToMany(mappedBy="userId")
private List<User> users;
#OneToOne(mappedBy="userId")
private UserDetail detail;
}
#Entity
public class User {
#Id
private Long _id;
#ManyToOne
private UserId userId;
}
#Entity
public class UserDetail {
#OneToOne
private UserId userId;
}
This way, you can somewhat easily navigate from users to details and back.
I came to a solution, that is not really satisfying, but works. I created a UUID field userId, which is not bound to an Entity and made sure, it is set only in the constructor.
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#Column(nullable = false)
private UUID id;
#Column(nullable = false)
// no setter for this field
private UUID userId;
#Column(nullable = false)
private boolean married;
public UserDetail(User user, boolean isMarried) {
this.id = UUID.randomUUID();
this.userId = user.getId();
this.married = isMarried;
}
// Constructors, Getters, Setters, ...
}
I dislike the fact, that I cannot rely on the database, to synchronize the userId, but as long as I stick to the no setter policy, it should work pretty well.

Hibernate Exception: Trying to map a Collection OneToMany without JoinTable

I'm trying to map an entity the contains a Collection without use JoinTable.
The exception that I keep having is:
org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: Foo.collection[java.lang.String]
I know that it works with JoinTable, but then I get 2 tables while I want only one table. I know that if it worked then I'd have the "name" many times in the table (one for each element in the collection).
I looked in the documentation (http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-mapping-association see section: 2.2.5.3.1.2. Unidirectional and http://docs.oracle.com/javaee/6/api/javax/persistence/OneToMany.html)and many examples but yet I cant find what I'm doing wrong :(
I also found many questions on this issue where the common answer is to use JoinTable, but this is not the solution I'm looking for.
I tries playing with it, like using #JoinColumn(name = "ID", referencedColumnName="NAME") but no good..
I'm using Java6, jpa2, hibernate 3.5.4. and MySql
Any advice?
Thanks in advance,
Baba
#Entity
#Table(name = "T1")
public class Foo {
private long id;
private String name;
protected Collection<String> collection;
#Id
#GeneratedValue
#Column(name = "ID")
public long getId() {
return id;
}
#Basic
#Column(name = "NAME", nullable = false, unique = true)
public String getName() {
return name;
}
#Column(name = "COLLECTION")
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ID")
public Collection<String> getCollection() {
return collection;
}
/*
* setters...
*/
}
This is in Hibernate docs.
#ElementCollection
#CollectionTable(name="Nicknames", joinColumns=#JoinColumn(name="user_id"))
#Column(name="nickname")
public Set<String> getNicknames() { ... }

Hibernate with JPA annotation problem - lazy object

I have a USER table associated with many other tables, in general, star topology.
Like this:
#Entity
#Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "user_USERID_GENERATOR", sequenceName = "user_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userR_USERID_GENERATOR")
#Column(name = "user_id")
private long userId;
#Basic
#Column(name = "password_hex")
private String password;
#Basic
#Column(name = "language")
private String language;
#Temporal(TemporalType.DATE)
private Date created;
#Temporal(TemporalType.DATE)
private Date modyfied;
#Basic
#Column(name = "first_name")
private String firstName;
#Basic
#Column(name = "last_name")
private String lastName;
#Basic
#Column(name = "passport")
private String passport;
#Basic
#Column(name = "pesel")
private String pesel;
#Basic
#Column(name = "phone_nr1")
private String phoneNr1;
#Basic
#Column(name = "phone_nr2")
private String phoneNr2;
#Column(name = "hash")
private String hash;
// uni-directional many-to-one association to DictUserType
#ManyToOne
#JoinColumn(name = "status")
private DictUserStatus status;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.ALL })
private Set<Email> emails = new HashSet<Email>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.ALL })
private Set<Address> address = new HashSet<Address>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.ALL })
private Set<ArchivePasswords> archivePasswords = new HashSet<ArchivePasswords>(
0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.ALL })
private Set<HostsWhitelist> hostsWhitelist = new HashSet<HostsWhitelist>(0);
....
I have a DAO layer, the method of search by user ID.
public User findUser(long userId) throws UserNotFoundException {
User user = userDao.findUser(userId);
if (user == null) {
throw new UserNotFoundException("Could not find user with id = "
+ userId);
}
return user;
}
Why lazy fetching does not work?
You should post the stack trace you are receiving. Where is the LazyLoadingException occurring? On the user Object? Are you trying to access it from another Object?
Is this the notorious LazyInitializationException? If so, then you need to either traverse the Object graph manually in the service (assuming you DAO code snippet is actually a Service method and not the DAO itself), or research the OpenSessionInViewFilter (assuming you are using Spring).
If you want to fetch the user with emails.
#Transactional
public List getUserWithEmails(long userId){
User user = userDao.findUser(userId);
if (user == null) {
throw new UserNotFoundException("Could not find user with id = "
+ userId);
}
for(Email email:user.getEmails()){
email.getId();
}
return user;
}
The same procedure apply to other one-to-many sets. Just like others have stated, you need to add OpenSessionInView (Hibernate) filter or OpenEntityManagerInView (JPA)filter in web.xml
If not specified, lazy fet hing will not take place defaults to EAGER.
public #interface Basic
The simplest type of mapping to a database column. The Basic annotation can be applied to a persistent property or instance variable of any of the following types: Java primitive types, wrappers of the primitive types, String, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, byte[], Byte[], char[], Character[], enums, and any other type that implements java.io.Serializable.
The use of the Basic annotation is optional for persistent fields and properties of these types. If the Basic annotation is not specified for such a field or property, the default values of the Basic annotation will apply.
Example 1:
#Basic
protected String name;
Example 2:
#Basic(fetch=LAZY)
protected String getName() { return name; }
fetch
public abstract FetchType fetch
(Optional) Defines whether the value of the field or property should be lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the value must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime. If not specified, defaults to EAGER.
Default:
javax.persistence.FetchType.EAGER
optional
public abstract boolean optional
(Optional) Defines whether the value of the field or property may be null. This is a hint and is disregarded for primitive types; it may be used in schema generation. If not specified, defaults to true.
Default:
true

Categories