java .lang.StackOverflowError При обновлении сущности со многими ко многим отношениям - PullRequest
1 голос
/ 18 апреля 2020

Таким образом, у меня есть 3 сущности, Employee, Shop и EmployeeShop, которые являются ссылочной сущностью для Employee и Shop.

@Entity
@Table(name = "employee_shops")
class EmployeeShop(

    @Id
    @ManyToOne
    var employee: Employee? = null,

    @Id
    @ManyToOne
    var shop: Shop? = null

) : Serializable {
    override fun equals(other: Any?): Boolean {
        if (this === other) {
            return true
        }
        if (other == null || javaClass != other.javaClass) {
            return false
        }
        other as EmployeeShop
        return Objects.equals(employee, other.employee) &&
            Objects.equals(shop, other.shop)
    }

    override fun hashCode(): Int {
        return Objects.hash(employee, shop)
    }
}

Сотрудник

@Entity
@Table(name = "employees")
class Employee(

    @Id
    var id: Int? = null,

    var userName: String? = null,
    var email: String? = null

    @OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL], orphanRemoval = true)
    var shops: MutableList<EmployeeShop> = mutableListOf()

) {
  override fun equals(other: Any?): Boolean {
    if (this === other) {
        return true
    }
    if (other == null || javaClass != other.javaClass) {
        return false
    }
    other as Employee
    return Objects.equals(userName, other.userName) &&
        Objects.equals(email, other.email)
  }

  override fun hashCode(): Int {
    return Objects.hash(userName, email)
  }
}

И Магазин

@Entity
@Table(name = "shops")
class Shop(

    @Id
    var id: Int? = null,

    @OneToMany(mappedBy = "shop", cascade = [CascadeType.ALL], orphanRemoval = true)
    var users: MutableList<EmployeeShop> = mutableListOf(),

) {
  override fun equals(other: Any?): Boolean {
    if (this === other) {
        return true
    }
    if (other == null || javaClass != other.javaClass) {
        return false
    }
    other as Shop
    return Objects.equals(id, other.id)
  }

  override fun hashCode(): Int {
    return Objects.hash(id)
  }
}

EmployeeRepository выглядит примерно так

interface EmployeeRepository: CrudRepository<Employee, String> {
    @Query(value = "SELECT e FROM Employee e LEFT JOIN FETCH e.shops WHERE e.id = :id")
    override fun findById(id: String): Optional<Employee>
}

Причина, по которой я переопределяю это, заключается в добавлении LEFT JOIN FETCH, чтобы избежать LazyInitializationException

Чтобы вернуться из API, я преобразую Employee в EmployeeDto

fun getDto(entity: Employee): EmployeeDto {
    return EmployeeDto(
        userName = entity.userName,
        email = entity.email
        shops = entity.shops.map { IdAndNameDto(it.shop?.id?.toString().orEmpty(), it.shop?.name.orEmpty()) }
    )
}

class IdAndNameDto(val id: String = "", val name: String = "")

class EmployeeDto(
    val userName: String,
    val email: String,
    val shops: List<IdAndNameDto>?
)

Когда я использую репозиторий в , обновление * Employee

employeeRepo.save(employee)

save от CrudRepository ( do c). Здесь save должен возвращать экземпляр Employee.

Я получаю следующую трассировку стека

java.lang.StackOverflowError: null
    at ch.qos.logback.classic.spi.TurboFilterList.getTurboFilterChainDecision(TurboFilterList.java:49) ~[logback-classic-1.2.3.jar:na]
    at ch.qos.logback.classic.LoggerContext.getTurboFilterChainDecision_0_3OrMore(LoggerContext.java:269) ~[logback-classic-1.2.3.jar:na]
    at ch.qos.logback.classic.Logger.callTurboFilters(Logger.java:751) ~[logback-classic-1.2.3.jar:na]
    at ch.qos.logback.classic.Logger.isTraceEnabled(Logger.java:623) ~[logback-classic-1.2.3.jar:na]
    at org.apache.logging.slf4j.SLF4JLogger.isEnabledFor(SLF4JLogger.java:213) ~[log4j-to-slf4j-2.12.1.jar:2.12.1]
    at org.apache.logging.slf4j.SLF4JLogger.isEnabled(SLF4JLogger.java:121) ~[log4j-to-slf4j-2.12.1.jar:2.12.1]
    at org.apache.logging.log4j.spi.AbstractLogger.isEnabled(AbstractLogger.java:1505) ~[log4j-api-2.12.1.jar:2.12.1]
    at org.jboss.logging.Log4j2Logger.isEnabled(Log4j2Logger.java:46) ~[jboss-logging-3.4.1.Final.jar:3.4.1.Final]
    at org.jboss.logging.Logger.isTraceEnabled(Logger.java:98) ~[jboss-logging-3.4.1.Final.jar:3.4.1.Final]
    at org.jboss.logging.DelegatingBasicLogger.isTraceEnabled(DelegatingBasicLogger.java:54) ~[jboss-logging-3.4.1.Final.jar:3.4.1.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:502) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:208) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:332) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:108) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:74) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:113) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1176) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1041) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:687) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.EntityType.resolve(EntityType.java:464) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.ManyToOneType.resolve(ManyToOneType.java:240) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.EntityType.resolve(EntityType.java:457) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:695) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:878) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:732) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.processResultSet(Loader.java:1008) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:964) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:324) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2410) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:74) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:63) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4396) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4386) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:569) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:537) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:208) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:332) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:108) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:74) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:113) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1176) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1041) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:687) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.EntityType.resolve(EntityType.java:464) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.ManyToOneType.resolve(ManyToOneType.java:240) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.EntityType.resolve(EntityType.java:457) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:695) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:878) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:732) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.processResultSet(Loader.java:1008) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:964) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:324) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2410) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:74) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:63) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4396) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4386) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
...

Остальная часть трассировки стека просто повторяет эти несколько строк, которые вы также можно увидеть из приведенного выше стека трассировки

at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:502) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:208) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:332) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:108) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:74) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:113) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1176) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1041) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:687) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.EntityType.resolve(EntityType.java:464) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.ManyToOneType.resolve(ManyToOneType.java:240) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.EntityType.resolve(EntityType.java:457) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.ComponentType.resolve(ComponentType.java:695) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:878) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:732) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.processResultSet(Loader.java:1008) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.doQuery(Loader.java:964) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:324) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.Loader.loadEntity(Loader.java:2410) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:74) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:63) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4396) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4386) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]

Но у меня нет ошибок при попытке создать Сотрудника с тем же методом save.

Кроме того, это происходит только в том случае, если сущность сотрудника содержит непустой объект shops. Если shop пусто, что означает, что Employee, который я получил с помощью findById, не содержит записей в таблице ссылок EmployeeShop, тогда обновление также не выдает ошибку.

Я все еще новичок в JPA, поэтому я подозреваю, что способ, которым я объявил эти сущности, например, как я использую аннотации, не верен. Но я не уверен, в чем проблема.

Кто-нибудь знает, почему я получил ошибку StackOverflow?

Ответы [ 2 ]

1 голос
/ 18 апреля 2020

Я не могу заметить ни одной очевидной ошибки с вашей стороны, и, судя по комментариям, другие тоже не могут.

Итак, у вас есть две вещи, которые вы можете / должны сделать:

  1. Упростите пример:

    • Удалите код (например, равно, hashcode, свойства), чтобы получить минимальный пример и, следовательно, также понять, что может вызвать эту проблему.
    • Попробуйте преобразовать его в Java, хотя я ожидаю, что это заставит проблему исчезнуть, но это только предположение.
    • Воспроизведите его непосредственно в Hibernate, без Spring Data.
  2. установить точку останова в месте, которое повторяется снова и снова в трассировке стека. Используйте отладчик для проверки значений и пошагового выполнения кода, часто это даст вам хотя бы приблизительное представление о том, что происходит не так, и поэтому может дать вам представление для обхода.

  3. Если вы все еще не нашли ошибку, используйте созданный на первом шаге репродуктор как ошибку с Hibernate или Spring Data, в зависимости от ваших выводов на первом шаге.

0 голосов
/ 03 мая 2020

Я случайно решил это, используя подход "пробовать все, что я могу".

  1. Я удалил класс сущности EmployeeShop.
  2. В классе Employee я внес это изменение.

    // old
    @OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL], orphanRemoval = true)
    var shops: MutableList<EmployeeShop> = mutableListOf()
    
    // new
    @ManyToMany
    @JoinTable(
        name = "employee_shops",
        joinColumns = [JoinColumn(name = "employee_id")],
        inverseJoinColumns = [JoinColumn(name = "shop_id")]
    )
    var shops: MutableSet<Shop> = mutableSetOf()
    
  3. И в классе Shop

    // old
    @OneToMany(mappedBy = "shop", cascade = [CascadeType.ALL], orphanRemoval = true)
    var users: MutableList<EmployeeShop> = mutableListOf()
    
    // new
    @ManyToMany(mappedBy = "shops")
    var users: MutableSet<Employee> = mutableSetOf()
    

Затем исключение исчезло, и приложение снова вернулось к работе. Мои знания Hibernate / JPA не достаточны, чтобы объяснить, почему это работает. Но если у кого-то есть такая же проблема, это может быть решением для вас.

...