Java 序列化懒散加载的Hibernate表会导致异常
我有一个相当标准的用例,它给了我非标准的问题 我在服务器上运行了一个Java web应用程序(使用Jersey和Hibernate)。它有一个从浏览器Javascript中调用的AJAX api 此服务将向ajax公开:Java 序列化懒散加载的Hibernate表会导致异常,java,hibernate,jpa,serialization,jersey,Java,Hibernate,Jpa,Serialization,Jersey,我有一个相当标准的用例,它给了我非标准的问题 我在服务器上运行了一个Java web应用程序(使用Jersey和Hibernate)。它有一个从浏览器Javascript中调用的AJAX api 此服务将向ajax公开: import list.nice.bll.UserBLL; import list.nice.dal.dto.Token; import list.nice.dal.dto.User; import org.glassfish.jersey.media.multipart.Fo
import list.nice.bll.UserBLL;
import list.nice.dal.dto.Token;
import list.nice.dal.dto.User;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
@Path("/users")
public class UserInfoService {
@POST
@Path("/getUser")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response getUserFromLogin(JAXBElement<User> user){
User rUser = user.getValue();
rUser = UserInfoService.getActiveUser(rUser.getUserID());
return Response.status(Response.Status.OK).header("Access-Control-Allow-Origin", "*").entity(UserInfoService.getActiveUser(rUser.getUserID())).build();
}
}
到这个函数的Ajax调用最终得到一个500内部服务器错误,但是服务器没有给我太多的数据(并且它继续运行,好像什么都没有发生)。调试控制台中打印的内容如下:
Feb 12, 2016 1:49:13 PM org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl useContextualLobCreation
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
Feb 12, 2016 1:49:13 PM org.hibernate.type.BasicTypeRegistry register
INFO: HHH000270: Type registration [java.util.UUID] overrides previous : org.hibernate.type.UUIDBinaryType@6505e696
Feb 12, 2016 1:49:13 PM org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
name: myapp.mypackage
...]
Feb 12, 2016 1:49:14 PM org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl useContextualLobCreation
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
Feb 12, 2016 1:49:14 PM org.hibernate.type.BasicTypeRegistry register
INFO: HHH000270: Type registration [java.util.UUID] overrides previous : org.hibernate.type.UUIDBinaryType@6505e696
Feb 12, 2016 1:49:14 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
但是,我不认为这是有用的信息,因为如果我将加载切换为“渴望”,它会在控制台中给出相同的消息,但效果很好
除此之外,我基本上完全迷失了方向,但为了完整起见,我尝试了一件同样无效的事情:
我决定试一试自定义XMLAdapter,因为我在调试时注意到,friends
是一个PersistentSet
,我想Jersey可能处理得不太好
所以我改变了用户类,如下所示:
@XmlJavaTypeAdapter(FriendAdapter.class)
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "friendships", joinColumns = @JoinColumn(name="requesteruserID"), inverseJoinColumns = @JoinColumn(name="requesteduserID"))
@WhereJoinTable(clause = "accepted = 'TRUE'")
private Set<User> friends = new HashSet<User>();
@XmlJavaTypeAdapter(FriendAdapter.class)
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(name=“friendships”、joinColumns=@JoinColumn(name=“requesteruserID”)、inverseJoinColumns=@JoinColumn(name=“requesteduserID”))
@其中jointable(子句=“已接受=”真“)
private Set friends=new HashSet();
FriendAdapter是这样的:
@Entity
@Table(name="users")
public class User {
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name="increment", strategy = "increment")
private int userID;
private String name;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "friendships", joinColumns = @JoinColumn(name="requesteruserID"), inverseJoinColumns = @JoinColumn(name="requesteduserID"))
@WhereJoinTable(clause = "accepted = 'TRUE'")
private Set<User> friends = new HashSet<User>();
public User(){}
public User(int userID, String name) {
this.userID = userID;
this.name = name;
}
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<User> getFriends() {
return friends;
}
public void setFriends(Set<User> friends) {
this.friends = friends;
}
protected static User getActiveUser(int userID) {
EntityManager entityManager = HibernateUtil.getEntityManagerFactory().createEntityManager();
User user = (User) entityManager.createQuery("from User where userID = :userID").setParameter("userID", userID).getSingleResult();
user.getFriends();
//I have also tried Hibernate.initialize(user.getFriends())
entityManager.close();
return user;
}
import org.hibernate.collection.internal.PersistentSet;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.util.*;
public class FriendAdapter extends XmlAdapter<List<User>, Set> {
@Override
public Set unmarshal(List<Friend> v) throws Exception {
return new HashSet<Friend>(v);
}
@Override
public List<Friend> marshal(Set v) throws Exception {
PersistentSet p = (PersistentSet) v;
if(p.empty()) {
return null;
}
return new ArrayList<Friend>(Arrays.asList((Friend[])v.toArray(new Friend[0])));
}
}
import org.hibernate.collection.internal.PersistentSet;
导入javax.xml.bind.annotation.adapters.XmlAdapter;
导入java.util.*;
公共类FriendAdapter扩展了XmlAdapter{
@凌驾
公共集解组(列表v)引发异常{
返回新的HashSet(v);
}
@凌驾
公共列表封送处理程序(集合v)引发异常{
PersistentSet p=(PersistentSet)v;
if(p.empty()){
返回null;
}
返回新的ArrayList(Arrays.asList((Friend[])v.toArray(new Friend[0]);
}
}
这给了我一个非常奇怪的结果:在序列化之后,进行ajax调用的web浏览器将获得一个字符串(而不是普通的对象数组),该字符串的内容是“myapp.mypackage”。User@3c81a180myapp.mypackage。User@28d2a9cfmyapp.mypackage。User@19c74a79“
我该怎么做才能度过这一关?急切加载修复了所有问题,但我不想加载朋友的朋友的朋友。因此,这并不是世界上最好的解决方案,更具战略性地使用XMLMapper也可能奏效,但这也同样有效。我的最终问题是Hibernate相对糟糕的代理系统 我从这些帮助我走向正确方向的其他答案中拼凑出这个解决方案: 为了克服Hibernate代理系统,我调整了我的用户类,添加了一个别名,以便序列化程序不会试图直接访问好友列表(因为它是Hibernate代理):
@实体
@表(name=“users”)
公共类用户{
@身份证
@生成值(生成器=“增量”)
@GenericGenerator(name=“increment”,strategy=“increment”)
私有int用户id;
私有字符串名称;
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(name=“friendships”、joinColumns=@JoinColumn(name=“requesteruserID”)、inverseJoinColumns=@JoinColumn(name=“requesteduserID”))
@其中jointable(子句=“已接受=”真“)
private Set friends=new HashSet();
@短暂的
私有集;
@短暂的
私有布尔friendListInitialized=false;
公共用户(){}
公共用户(int userID,字符串名称){
this.userID=userID;
this.name=名称;
}
public int getUserID(){
返回用户标识;
}
public void setUserID(int userID){
this.userID=userID;
}
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公共集getFriends(){
回报朋友;
}
公共无效设置好友(设置好友){
this.FriendsListinInitialized=false;
this.friendsListSnapshot=null;
这个。朋友=朋友;
}
公众朋友(){
((PersistentSet)friends.forceInitialization();
this.friendsListSnapshot=((Map)((PersistentSet)friends.getStoredSnapshot()).keySet();
this.FriendsListinInitialized=true;
}
}
如果有人知道为什么PersistentSet可以在其加载时序列化,而不是在其加载时序列化,然后强制初始化,请插话。因此,这并不是世界上最好的解决方案,更具战略性的使用XMLMappers可能也行得通,但这也行得通。我的最终问题是Hibernate相对糟糕的代理系统 我从这些帮助我走向正确方向的其他答案中拼凑出这个解决方案: 为了克服Hibernate代理系统,我调整了我的用户类,添加了一个别名,以便序列化程序不会试图直接访问好友列表(因为它是Hibernate代理):
@实体
@表(name=“users”)
公共类用户{
@身份证
@生成值(生成器=“增量”)
@GenericGenerator(name=“increment”,strategy=“increment”)
私有int用户id;
私有字符串名称;
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(name=“friendships”、joinColumns=@JoinColumn(name=“requesteruserID”)、inverseJoinColumns=@JoinColumn(name=“requesteduserID”))
@其中jointable(子句=“已接受=”真“)
private Set friends=new HashSet();
@短暂的
私有集;
@短暂的
私有布尔friendListInitialized=false;
公共用户(){}
公共用户(int userID,字符串名称){
this.userID=userID;
this.name=名称;
}
public int getUserID(){
返回用户标识;
}
@Entity
@Table(name="users")
public class User {
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name="increment", strategy = "increment")
private int userID;
private String name;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "friendships", joinColumns = @JoinColumn(name="requesteruserID"), inverseJoinColumns = @JoinColumn(name="requesteduserID"))
@WhereJoinTable(clause = "accepted = 'TRUE'")
private Set<User> friends = new HashSet<User>();
@Transient
private Set<User> friendListSnapshot;
@Transient
private boolean friendListInitialized = false;
public User(){}
public User(int userID, String name) {
this.userID = userID;
this.name = name;
}
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<User> getFriends() {
return friends;
}
public void setFriends(Set<User> friends) {
this.friendsListInitialized = false;
this.friendsListSnapshot = null;
this.friends = friends;
}
public void initFriends(){
((PersistentSet)friends).forceInitialization();
this.friendsListSnapshot = ((Map<User, ?>)((PersistentSet) friends).getStoredSnapshot()).keySet();
this.friendsListInitialized = true;
}
}