Hibernate Envers AuditQuery добавляет _id к столбцу mappedBy и завершается с ошибкой, исключение "невозможно разрешить свойство" - PullRequest
0 голосов
/ 16 сентября 2018

Я использую Envers для аудита моих юридических лиц.Но когда я использую AuditQuery для получения ревизий моей сущности вместе со связанной сущностью, происходит сбой со следующим исключением:

could not resolve property: userid_id of: com.envers.domain.vo.UserOrgVO_AUD [select e__ from com.envers.domain.vo.UserOrgVO_AUD e__ where e__.userid_id = :userid_id and e__.originalId.REVISION_ID.id = (select max(e2__.originalId.REVISION_ID.id) from com.envers.domain.vo.UserOrgVO_AUD e2__ where e2__.originalId.REVISION_ID.id <= :revision and e__.originalId.id = e2__.originalId.id) and REVISION_TYPE != :delrevisiontype]

Мои сущности выглядят следующим образом:

Пользователь (id: Long, имя: String, организации: Set [UserOrganisation])

UserOrganisation (id: Long, идентификатор пользователя: Long, организация:Организация)

Организация (id: Long, name: String)

Классы VO определены следующим образом

UserVO

@Entity(name = "USERS")
@Audited
public class UserVO {
  @Id
  @Column(name = "id")
  private Long id;

  @Column(nullable = false)
  private String name;

  @OneToMany (fetch = FetchType.EAGER, mappedBy = "userid", cascade = CascadeType.ALL, orphanRemoval = true)
  @Audited
  private List<UserOrgVO> organisations;

  /**............................................................

     constructors, getters and setters ....

     ............................................................*/
}

UserOrgVO

@Entity(name = "USER_ORGANISATIONS")
@Audited
public class UserOrgVO {
  @Id
  @Column(name = "ID")
  private Long id;

  @Column (name = "USER_ID")
  private Long userid;

  @ManyToOne
  @JoinColumn (name = "ORGANISATION_ID", nullable = false)
  private OrganisationVO organisation;

  @Column (name = "PREFERRED", nullable = false)
  private boolean preferred = false;

  /**............................................................

     constructors, getters and setters ....

     ............................................................*/
}

OrganisationVO

@Entity(name = "ORGANISATIONS")
@Audited
public class OrganisationVO {
  @Id
  private Long id;

  @Column(nullable = false, name = "NAME", length = 70)
  private String name;

  /**............................................................

     constructors, getters and setters ....

     ............................................................*/
}

Функция, которая пытается извлечь ревизии пользователя, выглядит следующим образом:

@Override
public List<UserVO> findAllRevisions(Long id) {
  AuditReader reader = AuditReaderFactory.get(entityManager);
  AuditQuery query = reader.createQuery().forRevisionsOfEntity(UserVO.class, true, true);
  query.add(AuditEntity.id().eq(id));
  List revisions = query.getResultList();

  List<UserVO> users = new ArrayList<>();
  for(Object rev : revisions) {
    UserVO userRev = (UserVO) rev;
    for(UserOrgVO userOrgRev : userRev.getOrganisations()) {
      System.out.println("user org id: " + userOrgRev.getId());
    }
    users.add(userRev);
  }

  return users;
}

Строка userRev.getOrganisations () в приведенной выше функции вызывает следующее исключение

ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.hibernate.QueryException: could not resolve property: userid_id of: com.envers.domain.vo.UserOrgVO_AUD [select e__ from com.envers.domain.vo.UserOrgVO_AUD e__ where e__.userid_id = :userid_id and e__.originalId.REVISION_ID.id = (select max(e2__.originalId.REVISION_ID.id) from com.envers.domain.vo.UserOrgVO_AUD e2__ where e2__.originalId.REVISION_ID.id <= :revision and e__.originalId.id = e2__.originalId.id) and REVISION_TYPE != :delrevisiontype]] with root cause
org.hibernate.QueryException: could not resolve property: userid_id of: com.envers.domain.vo.UserOrgVO_AUD
at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:62)
at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:56)
at org.hibernate.persister.entity.AbstractEntityPersister.toType(AbstractEntityPersister.java:1764)
at org.hibernate.hql.internal.ast.tree.FromElementType.getPropertyType(FromElementType.java:393)
at org.hibernate.hql.internal.ast.tree.FromElement.getPropertyType(FromElement.java:505)
at org.hibernate.hql.internal.ast.tree.DotNode.getDataType(DotNode.java:660)
at org.hibernate.hql.internal.ast.tree.DotNode.prepareLhs(DotNode.java:264)
at org.hibernate.hql.internal.ast.tree.DotNode.resolve(DotNode.java:204)
at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:109)
at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:104)
at org.hibernate.hql.internal.ast.HqlSqlWalker.resolve(HqlSqlWalker.java:942)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1283)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4696)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:4166)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2131)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2056)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2056)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:810)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:605)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:309)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:257)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:142)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:76)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:150)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:302)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:240)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1894)
at org.hibernate.envers.internal.entities.mapper.relation.query.AbstractRelationQueryGenerator.getQuery(AbstractRelationQueryGenerator.java:55)
at org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.AbstractCollectionInitializor.initialize(AbstractCollectionInitializor.java:49)
at org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.CollectionProxy.checkInit(CollectionProxy.java:33)
at org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.CollectionProxy.iterator(CollectionProxy.java:57)
at com.envers.service.impl.UserServiceImpl.findAllRevisions(UserServiceImpl.java:79)

Можете ли вы сказать, почему envers добавляет «_id» в столбец «userid» в UserOrgVO и как это исправить?

Спасибо и с уважением,

Sarath

1 Ответ

0 голосов
/ 19 сентября 2018

Я думаю, что проблема с вашими отображениями.

Вы пытаетесь определить двунаправленное отображение между UserVO и UserOrgVO, но проблема в том, что вы указываете только сторону @OneToMany и не указываете @ManyToOne, кому она принадлежит.

Я предполагаю, что причиной этого, вероятно, является оптимизация, чтобы предотвратить попытки Hibernate получить и загрузить объект UserVO, когда вы загружали UserOrgVO сущностей в другие части вашего кода, но это неправильный способ справиться с этим.

Технически ваше отображение должно быть следующим:

public class UserVO {
  ...
  @OneToMany(
    fetch = FetchType.EAGER, 
    mappedBy = "user", 
    cascade = CascadeType.ALL, 
    orphanRemoval = true)
  private List<UserOrgVO> organizations;
}

public class UserOrgVO {
  ..
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "USER_ID")
  private UserVO user;
}

Вы заметите, что я сопоставил user в UserOrgVO как FetchType.LAZY. Это означает, что Hibernate должен создать для вас прокси-сервер на основе значения из столбца USER_ID и будет извлекать и обрабатывать экземпляр UserVO только при первом вызове его метода get; таким образом делая это действительно ленивым.

Если вы действительно хотите, чтобы userid также отображался как свойство, к которому у вас есть доступ к сущности UserOrgVO, вы можете сделать это, дублируя столбец USER_ID в сущности UserOrgVO следующим образом:

@Column(name = "USER_ID", insertable = false, updatable = false)
private Long userid;

Предостережение заключается в том, что каждый раз, когда вы изменяете свойство user в UserOrgVO для ссылки на другой пользовательский объект, вы должны обновить значение userid, чтобы оно оставалось согласованным в контексте постоянства. Когда дело доходит до базы данных, Hibernate устанавливает значение только на основе того, с чем связан элемент UserVO, и полностью игнорирует значение в userid.

Короче говоря, вы можете думать о userid как о временном значении, которое Hibernate устанавливает для объекта, загружаемого из базы данных, и в противном случае с ним абсолютно ничего не делает. Это чисто информационный вопрос с этого момента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...