Я пытаюсь сохранить Comment
в спящем режиме.Помимо идентификатора и тела, у Comment
есть пользователь и свойство проблемы.Следующий код выдает org.hibernate.TransientObjectException
, сказав, что я должен сохранить User
перед сбросом.
public static long saveComment(Comment comment) {
long ret;
try (Session ses = factory.openSession()) {
ses.beginTransaction();
ses.save(comment.getUser());
ses.save(comment.getIssue());
ret = (long) ses.save(comment);
ses.getTransaction().commit();
}
return ret;
}
Изменение ses.save(comment.getUser());
на ses.persist(comment.getUser());
приводит к org.hibernate.PersistentObjectException
, говорящему, что User
отсоединен.Я в замешательстве, насколько я понимаю, объект в спящем режиме является либо временным, либо отсоединенным, но здесь он показывает, что они оба.
Я уже пробовал разные каскадные типы в аннотациях User
s OneToMany
,в настоящее время они установлены на PERSIST
, швы ведьмы, что я хочу.Кроме того, насколько я понимаю, Comment
не должно иметь значения cascade
, поскольку User
и Issue
владеют Comment
.
Ниже приведен код, который получает Пользователь икод, который вызывает saveComment
.
public static Response create(String body) throws IOException {
Map<String, Object> map = new ObjectMapper()
.configure(DeserializationFeature.USE_LONG_FOR_INTS, true)
.readValue(body, Map.class);
User creator;
Issue issue;
try {
creator = Repository.getUserById((long) map.get("user_id"));
} catch (NoResultException ex) {
return Response.status(404).entity(ex.getMessage()).build();
}
try {
issue = Repository.getIssueById((long) map.get("issue_id"));
} catch (NoResultException ex) {
return Response.status(404).entity(ex.getMessage()).build();
}
Comment comment = new Comment();
comment.setBody((String) map.get("body"));
comment.setIssue(issue);
comment.setUser(creator);
creator.addComment(comment);
issue.addComment(comment);
long id = Repository.saveComment(comment);
return Response.created(URI.create("/comment/"+id)).build();
}
public static User getUserById(long id) {
User ret;
try (Session ses = factory.openSession()) {
ses.beginTransaction();
ret = (User) ses.createQuery(
"from User u where u.id = :id")
.setParameter("id", id)
.getSingleResult();
ses.getTransaction().commit();
}
return ret;
}
РЕДАКТИРОВАТЬ: сущности пользователя и комментария.
package de.phs.issues.model;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Entity
@Data
@EqualsAndHashCode(exclude = {"issues", "comments"})
@Table(name="`user`")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = User.class)
public class User {
@Column(nullable = false, unique = true)
private String name;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "creator", cascade = CascadeType.PERSIST)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
private Set<Issue> issues = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER, mappedBy = "user", cascade = CascadeType.PERSIST)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
private Set<Comment> comments = new HashSet<>();
public String toJson() throws JsonProcessingException {
return new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.writeValueAsString(this);
}
public static User fromJson(String json) throws IOException {
return new ObjectMapper()
.configure(DeserializationFeature.USE_LONG_FOR_INTS, true)
.readValue(json, User.class);
}
public boolean addIssue(Issue issue) {
issue.setCreator(this);
return issues.add(issue);
}
public boolean removeIssue(Issue issue) {
issue.setCreator(null);
return issues.remove(issue);
}
public boolean addComment(Comment comment) {
comment.setUser(this);
return comments.add(comment);
}
public boolean removeComment(Comment comment) {
comment.setUser(null);
return comments.remove(comment);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("User{id=");
sb.append(id);
sb.append(" ,name=");
sb.append(name);
sb.append(", issues=[");
String issue_ids = issues.stream()
.map(Issue::getId)
.map(l -> Long.toString(l))
.flatMap(s -> Stream.of(",", s))
.skip(1)
.collect(Collectors.joining());
sb.append(issue_ids);
sb.append("], comments=[");
String comment_ids = comments.stream()
.map(Comment::getId)
.map(l -> Long.toString(l))
.flatMap(s -> Stream.of(",", s))
.skip(1)
.collect(Collectors.joining());
sb.append(comment_ids);
sb.append("]}");
return sb.toString();
}
}
package de.phs.issues.model;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
import java.io.IOException;
@Data
@EqualsAndHashCode(exclude = {"user", "issue"})
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Comment.class)
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String body;
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
private User user;
@ManyToOne(fetch = FetchType.EAGER)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
private Issue issue;
public String toJson() throws JsonProcessingException {
return new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.writeValueAsString(this);
}
public static Comment fromJson(String json) throws IOException {
return new ObjectMapper()
.configure(DeserializationFeature.USE_LONG_FOR_INTS, true)
.readValue(json, Comment.class);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Comment{body=");
sb.append(body);
sb.append(", id=");
sb.append(id);
sb.append(", user=");
sb.append(user.getId());
sb.append(", issue=");
sb.append(issue.getId());
sb.append("}");
return sb.toString();
}
}