0%
这是一片思考的空间 -- arthinking
Spring 重构&代码整洁之道 软件设计 JVM 并发编程 数据结构与算法 分布式 存储 网络 微服务 设计模式
Java技术栈 - 涉及Java技术体系

JPA一对多关系重复数据问题导致的查询错误BeanSerializer

JPA重复数据问题:

两个实体类:

package com.itzhai.musicsystem.bean;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="t_user")
public class User {
@Id
@Column(name = "user_id")
private String username;
@Column(name = "user_nikename", length=100)
private String nikename;
@Column(name = "user_password", length=20)
private String password;
@Column(name = "user_email", length=40)
private String email;
//@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "creater", fetch = FetchType.EAGER)
//单向一对多
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.EAGER, mappedBy = "creater")
private Set albums;

public String getUsername() {
    return username;
}
public void setUsername(String username) {
    this.username = username;
}

public String getNikename() {
    return nikename;
}
public void setNikename(String nikename) {
    this.nikename = nikename;
}

public String getPassword() {
    return password;
}
public void setPassword(String password) {
    this.password = password;
}

public String getEmail() {
    return email;
}
public void setEmail(String email) {
    this.email = email;
}

public Set<Album> getAlbums() {
    return albums;
}
public void setAlbums(Set<Album> albums) {
    this.albums = albums;
}

}

package com.itzhai.musicsystem.bean;

import java.util.Date;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="t_album")
public class Album {

@Id
@Column(name = "album_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int albumId=0;

@Column(name = "album_name", length=100)
private String albumName;

@Column(name = "album_createtime", length=20)
private Date createTime;

@ManyToOne(cascade = { CascadeType.REFRESH, CascadeType.MERGE }, optional = false)
@JoinColumn(name = "user_id")
private User creater;

//@Column(name = "album_musics")
//@OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinTable(name = "t_album_t_music", 
        joinColumns = { @JoinColumn(name = "t_album_id", referencedColumnName = "album_id") },
        inverseJoinColumns = { @JoinColumn(name = "t_music_id", referencedColumnName = "music_id") })
private Set<Music> musics;

public int getAlbumId() {
    return albumId;
}
public void setAlbumId(int albumId) {
    this.albumId = albumId;
}

public String getAlbumName() {
    return albumName;
}
public void setAlbumName(String albumName) {
    this.albumName = albumName;
}

public Date getCreateTime() {
    return createTime;
}
public void setCreateTime(Date createTime) {
    this.createTime = createTime;
}

public User getCreater() {
    return creater;
}
public void setCreater(User creater) {
    this.creater = creater;
}

public Set<Music> getMusics() {
    return musics;
}
public void setMusics(Set<Music> musics) {
    this.musics = musics;
}

}

使用Jackson框架转换:

    ObjectMapper objectMapper = new ObjectMapper();
    JsonGenerator jsonGenerator = null;
    String result;
    try {
        jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(System.out, JsonEncoding.UTF8);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    QueryResult<Album> albumList = new QueryResult<Album>();
    albumList = ManagerHelper.getAlbumManager().getScrollData(Album.class);

    try {
        jsonGenerator.writeObject(albumList.getResultlist());
    } catch (JsonProcessingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

发现得出如下的数据:

[{"albumId":1,"musics":[{"id3v1":"TAG","songName":"红日\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","artist":"李克勤\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","album":"留住这一刻-李克勤1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","genre":0,"musicUrl":"http://localhost/music/李克勤-红日.mp3","id":7,"size":4643195,"comment":"\u0000\u0001\f ","year":"\u0000\u0000\u0000\u0000"}],"albumName":"abcdefg","createTime":1335692164000,"creater":{"username":"arthinking","nikename":"abc","email":"arthinking@qq.com","albums":[{"albumId":2,"musics":[{"id3v1":"TAG","songName":"千千厥歌\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","artist":"陳慧嫻\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","album":"柔情金曲精装集\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","genre":0,"musicUrl":"http://localhost/music/陈慧娴 - 千千厥歌.mp3","id":15,"size":8436425,"comment":"\u0000\u0000?\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","year":"\u0000\u0000\u0000\u0000"}],"albumName":"gggggsfeffs","createTime":1335693885000,"creater":{"username":"arthinking","nikename":"abc","email":"arthinking@qq.com","albums":[{"albumId":2,"musics":[{"id3v1":"TAG","songName":"千千厥歌\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","artist":"陳慧嫻\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","album":"柔情金曲精装集\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","genre":0,"musicUrl":"http://localhost/music/陈慧娴 - 千千厥歌.mp3","id":15,"size":8436425,"comment":"\u0000\u0000?\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","year":"\u0000\u0000\u0000\u0000"}],"albumName":"gggggsfeffs","createTime":1335693885000,"creater":{"username":"arthinking","nikename":"abc","email":"arthinking@qq.com","albums":[{"albumId":2,"musics":[{"id3v1":"TAG","songName":"千千厥歌\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","artist":"陳慧嫻\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","album":"柔情金曲精装集\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","genre":0,"musicUrl":"http://localhost/music/陈慧娴 - 千千厥歌.mp3","id":15,"size":8436425,"comment":"\u0000\u0000?\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","year":"\u0000\u0000\u0000\u0000"}],"albumName":"gggggsfeffs","createTime":1335693885000,"creater":{"username":"arthinking","nikename":"abc","email":"arthinking@qq.com",此处省略n个字

之后便是一直报错:

at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)

从输出的结果可以看出是查处了不断重复的数据。

经过进一步分析发现上面的数据中Album中的creater里面又有Album,这样不断的重复获取数据,就形成了无限循环。

为了解决这个问题,就是让use成为维护端,这样从user这边查出的所有Album就不会重复了。

或者改成由Album到user的多对一关系,而不是由create到Album的一对多关系,既是把user中的album集合删除,Album成为维护端:

保留Album中的配置即可,这样就可以从Album端查出数据了。生成的表只是在t_album加了个user_id外键。

根据表的模型进行配置,不要为了ORM而ORM。根据自己的需要选择合理的配置和查询方法,这样才能写出高效的查询代码。根据自己的表的设计,查阅到到相关的配置方法进行配置,这样才能从本质上提高自己的数据库设计能力。

欢迎关注我的其它发布渠道

订阅IT宅
内功修炼
Java技术栈
Java架构杂谈是IT宅精品文章公众号,欢迎订阅:
📄 网络基础知识:两万字长文50+张趣图带你领悟网络编程的内功心法 📄 HTTP发展史:三万长文50+趣图带你领悟web编程的内功心法 📄 HTTP/1.1:可扩展,可靠性,请求应答,无状态,明文传输 📄 HTTP/1.1报文详解:Method,URI,URL,消息头,消息体,状态行 📄 HTTP常用请求头大揭秘 📄 HTTPS:网络安全攻坚战 📄 HTTP/2:网络安全传输的快车道 📄 HTTP/3:让传输效率再一次起飞 📄 高性能网络编程:图解Socket核心内幕以及五大IO模型 📄 高性能网络编程:三分钟短文快速了解信号驱动式IO 📄 高性能网络编程:彻底弄懂IO复用 - IO处理杀手锏,带您深入了解select,poll,epoll 📄 高性能网络编程:异步IO:新时代的IO处理利器 📄 高性能网络编程:网络编程范式 - 高性能服务器就这么回事 📄 高性能网络编程:性能追击 - 万字长文30+图揭秘8大主流服务器程序线程模型
📄 Java内存模型:如果有人给你撕逼Java内存模型,就把这些问题甩给他 📄 一文带你彻底理解同步和锁的本质(干货) 📄 AQS与并发包中锁的通用实现 📄 ReentrantLock介绍与使用 📄 ReentrantReadWriteLock介绍与使用 📄 ReentrantLock的Condition原理解析 📄 如何优雅的中断线程 📄 如何优雅的挂起线程 📄 图解几个好玩的并发辅助工具类 📄 图解BlockingQueue阻塞队列
📄 消息队列那么多,为什么建议深入了解下RabbitMQ? 📄 高并发异步解耦利器:RocketMQ究竟强在哪里? 📄 Kafka必知必会18问:30+图带您看透Kafka
📄 洞悉MySQL底层架构:游走在缓冲与磁盘之间 📄 SQL运行内幕:从执行原理看调优的本质 📄 洞悉Redis技术内幕:缓存,数据结构,并发,集群与算法