简体   繁体   中英

JPA infinitely recursion with bidirectional relationships in Kotlin and Glassfish

I have 2 data classes in kotlin, each having a reference to eachother. Profile and Kweet (Code will be at the bottom). When fetching one of these entities with an EntityManager it can succesfully fetch a single object. It will never return this however, as the JPA keeps fetching the recursive relationship in the background.

The issue occurs when either ProfileDao.getById or ProfileDao.getByScreenname is being called.

Profile.kt

@Entity(name = "profile")
@NamedQueries(
(NamedQuery(name = "Profile.getByScreenName", query = "select p from profile p where p.screenname LIKE :screenname")),
(NamedQuery(name = "Profile.getAll", query = "select p from profile p"))
)
data class Profile(
@Id
@GeneratedValue
var id: Int? = null,

var screenname: String,

var created: Timestamp
) {

@OneToMany(mappedBy = "profile", fetch = FetchType.LAZY, cascade = [CascadeType.DETACH])
var kweets: List<Kweet> = emptyList()

@ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.DETACH])
@JoinTable(
    name = "liked_kweets",
    joinColumns = [(JoinColumn(name = "profile_id", referencedColumnName = "id"))],
    inverseJoinColumns = [(JoinColumn(name = "kweet_id", referencedColumnName = "id"))]
)
var likes: List<Kweet> = emptyList()

@ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.DETACH])
@JoinTable(
    name = "follows",
    joinColumns = [(JoinColumn(name = "follower_id", referencedColumnName = "id"))],
    inverseJoinColumns = [(JoinColumn(name = "followed_id", referencedColumnName = "id"))]
)
var follows: List<Profile> = emptyList()

@ManyToMany(mappedBy = "follows", fetch = FetchType.LAZY, cascade = [CascadeType.DETACH])
var followers: List<Profile> = emptyList()
}

Kweet.kt

@Entity(name = "kweet")
@NamedQuery(name = "Kweet.getAll", query = "select k from kweet k")
data class Kweet(
@Id
@GeneratedValue()
var Id: Int? = null,
var created: Timestamp,
var message: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "profile_id")
@JsonBackReference
var profile: Profile,
@ManyToMany(mappedBy = "likes", fetch = FetchType.LAZY)
@JsonBackReference
var likedBy: List<Profile> = emptyList()
)

ProfileDao.kt

@Stateless
class ProfileDao {
@PersistenceContext
lateinit var em: EntityManager

fun getById(id: Int) = em.find(Profile::class.java, id)

fun getAll(): List<Profile> = em.createNamedQuery("Profile.getAll", Profile::class.java).resultList

fun getByScreenname(name: String) = em.createNamedQuery("Profile.getByScreenName", Profile::class.java)
    .setParameter("screenname", name)
    .resultList
    .firstOrNull()

fun create(profile: Profile) = em.persist(profile)

fun follow(follower: Profile, leader: Profile) {
    follower.follows += leader
    leader.followers += follower
    em.persist(follower)
    em.persist(leader)
}
}

Update: Adding a DTO and marking this as open correctly solves the recursion error. Example:

@Open
class ProfileFacade(
private val profile: Profile
) : Serializable {
var screenname: String
    get() = profile.screenname
    set(value) {
        profile.screenname = value
    }

var kweets: List<SimpleKweetFacade>
    get() = profile.kweets.map { SimpleKweetFacade(it) }
    set(value) = Unit

var follows: List<String>
    get() = profile.follows.map { it.screenname }
    set(value) = Unit

var created: Timestamp
    get() = profile.created
    set(value) = Unit
}

The @Open annotation is a simple annotation class Open() which is then processed by gradle to add open and noarg constructors

You should either use DTOs to represent data in the front end or @JsonIgnore parent references from child objects (instead of @JsonBackReference ).

Using DTOs is probably a smarter choice as you can decouple your front end presentation from the backend model, which gives you flexibility in both layers (ie. changing one layer does not break/possibly introduce bugs in the other).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM