Hibernate: исключение MappingException для отношений с двумя @ JoinColumn с дополнительным отображением PK только для чтения для одного из его столбцов - PullRequest
0 голосов
/ 04 января 2019

Остерегайтесь следующего дизайна БД:

DB design

ПРИМЕЧАНИЕ : Не берите в голову то, что имел создатель этого "дизайна"в уме, но я не могу изменить его.

Речь идет об отношениях между MobileTans и Authenticators ...

Соответствующие отображения таблицы MobileTans:

@Entity
@Table(name="MobileTans")
public class MobileTan implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="ID", insertable=false, updatable=false)
    private Integer id;    // dupe read-only field, ID already in relationship below (writable)

    @Basic(optional=false)
    @Column(name="MOBILE_PHONE_NBR")
    private String mobilePhoneNbr;

    @Basic
    @Column(name="ACTIVATION_CODE")
    private String activationCode;

    @Basic
    @Column(name="ACTIVATION_ERROR")
    private Boolean activationError = Boolean.FALSE;

    @Basic
    @Column(name="STATUS")
    private Integer status;

    @OneToOne(optional=false, fetch=FetchType.EAGER)
    @JoinColumn(name="ID", referencedColumnName="ID")
    @JoinColumn(name="AUTH_NAME", referencedColumnName="AUTHENTICATOR")
    private Authenticator authenticator;

    ...
}

Подсказка - дублирующее сопоставление только для чтения (insertable=false, updatable=false) для столбца @Id.

Столбец ID определен как PK, поэтому нет смысла вустановив @Id на отношения.Я должен создать дополнительное сопоставление столбцов.Я добавляю только чтение для дополнительного столбца, потому что Hibernate (или JPA) не позволяет мне указывать только чтение только для частей отношений (я думаю, что это запрещено спецификацией JPA).

РЕДАКТИРОВАТЬ: Аутентификатор сущности

@Entity
@Table(name="Authenticators")
@IdClass(AuthenticatorPk.class)
public class Authenticator implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="ID")
    private Integer id;

    @Id
    @Column(name="AUTHENTICATOR")
    private String authenticator = "";

    @Basic(optional=false)
    @Column(name="PROCEDURE")
    private String procedure;

    @Temporal(TemporalType.DATE)
    @Basic
    @Column(name="VALID_FROM")
    private Date validFrom;

    @Temporal(TemporalType.DATE)
    @Basic
    @Column(name="VALID_TO")
    private Date validTo;

    public Authenticator()
    {
    }

    ...
}

PK класс:

public class AuthenticatorPk implements Serializable
{
    private static final long serialVersionUID = 1L;

    private Integer id;

    private String authenticator = "";

    public AuthenticatorPk()
    {
    }

    ...
}

При запуске сервера я получаю исключение сопоставления из Hibernate:

00:37:53,979 INFO  [org.hibernate.Version] (ServerService Thread Pool -- 72) HHH000412: Hibernate Core {5.3.6.Final}
00:37:53,980 INFO  [org.hibernate.cfg.Environment] (ServerService Thread Pool -- 72) HHH000206: hibernate.properties not found
00:37:54,088 INFO  [org.hibernate.annotations.common.Version] (ServerService Thread Pool -- 72) HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
00:37:54,382 INFO  [org.hibernate.validator.internal.util.Version] (MSC service thread 1-2) HV000001: Hibernate Validator 6.0.13.Final
00:37:54,621 INFO  [org.jboss.weld.Version] (MSC service thread 1-3) WELD-000900: 3.0.5 (Final)
00:37:54,674 INFO  [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 72) WFLYCLINF0002: Started client-mappings cache from ejb container
00:37:54,777 INFO  [org.jboss.as.jpa] (ServerService Thread Pool -- 72) WFLYJPA0010: Starting Persistence Unit (phase 2 of 2) Service 'mappingbug.war#BBStatsPU'
00:37:54,806 INFO  [org.hibernate.dialect.Dialect] (ServerService Thread Pool -- 72) HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
00:37:54,859 INFO  [org.hibernate.envers.boot.internal.EnversServiceImpl] (ServerService Thread Pool -- 72) Envers integration enabled? : true
00:37:55,007 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 72) MSC000001: Failed to start service jboss.persistenceunit."mappingbug.war#BBStatsPU": org.jboss.msc.service.StartException in service jboss.persistenceunit."mappingbug.war#BBStatsPU": javax.persistence.PersistenceException: [PersistenceUnit: BBStatsPU] Unable to build Hibernate SessionFactory
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:195)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:125)
    at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:650)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:209)
    at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
    at java.lang.Thread.run(Thread.java:748)
    at org.jboss.threads.JBossThread.run(JBossThread.java:485)
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: BBStatsPU] Unable to build Hibernate SessionFactory
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1016)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:942)
    at org.jboss.as.jpa.hibernate5.TwoPhaseBootstrapImpl.build(TwoPhaseBootstrapImpl.java:44)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:167)
    ... 9 more
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: net.bbstats.entity.MobileTan column: ID (should be mapped with insert="false" update="false")
    at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:862)
    at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:880)
    at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:902)
    at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:634)
    at org.hibernate.mapping.RootClass.validate(RootClass.java:267)
    at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:347)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:466)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
    ... 11 more

00:37:55,011 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("deploy") failed - address: ([("deployment" => "mappingbug.war")]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.persistenceunit.\"mappingbug.war#BBStatsPU\"" => "javax.persistence.PersistenceException: [PersistenceUnit: BBStatsPU] Unable to build Hibernate SessionFactory
    Caused by: javax.persistence.PersistenceException: [PersistenceUnit: BBStatsPU] Unable to build Hibernate SessionFactory
    Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: net.bbstats.entity.MobileTan column: ID (should be mapped with insert=\"false\" update=\"false\")"}}
00:37:55,018 INFO  [org.jboss.as.server] (ServerService Thread Pool -- 42) WFLYSRV0010: Deployed "mappingbug.war" (runtime-name : "mappingbug.war")
00:37:55,019 INFO  [org.jboss.as.controller] (Controller Boot Thread) WFLYCTL0183: Service status report
WFLYCTL0186:   Services which failed to start:      service jboss.persistenceunit."mappingbug.war#BBStatsPU": javax.persistence.PersistenceException: [PersistenceUnit: BBStatsPU] Unable to build Hibernate SessionFactory
WFLYCTL0448: 19 additional services are down due to their dependencies being missing or failed
00:37:55,079 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
00:37:55,081 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
00:37:55,081 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
00:37:55,082 ERROR [org.jboss.as] (Controller Boot Thread) WFLYSRV0026: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) started (with errors) in 14146ms - Started 449 of 645 services (23 services failed or missing dependencies, 328 services are lazy, passive or on-demand)

Кстати, я использую Hibernate Core 5.3.6. Финал на Wildfly 14.

ВОПРОС :

Что здесь не так?Почему Hibernate жалуется на это?У меня сложилось впечатление, что это совершенно законно.

Другие шансы, что это ошибка Hibernate ...?

Обратите внимание, что мне больше интересно знать, должно ли это работать в Hibernate илине.Я не очень заинтересован в каких-либо не-JPA альтернативных отображениях.Я могу добиться этого, помещая в отношения только для чтения материал, тогда это работает, но не описанные выше отображения, которые должны ... вы получите это: -)

1 Ответ

0 голосов
/ 11 января 2019

Я могу почти получить то, что вы ищете, с помощью этого сопоставления здесь:

    @Entity
@Table(name = "mobile_tan")
public class MobileTan  {
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name="ID", insertable=false, updatable=false)
    private Integer id;    // dupe read-only field, ID already in relationship below (writable)


    @Basic(optional=false)
    @Column(name="MOBILE_PHONE_NBR")
    private String mobilePhoneNbr;


    @Basic
    @Column(name="ACTIVATION_CODE")
    private String activationCode;


    @Basic
    @Column(name="ACTIVATION_ERROR")
    private Boolean activationError = Boolean.FALSE;


    @Basic
    @Column(name="STATUS")
    private Integer status;


    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name="AUTH_NAME", referencedColumnName="AUTHENTICATOR")
    private Authenticator authenticator;
}

Однако, это создает таблицу со следующим определением (HSQL)

    create table mobile_tan (
    AUTH_NAME integer not null,
    ACTIVATION_CODE varchar(255),
    ACTIVATION_ERROR boolean,
    MOBILE_PHONE_NBR varchar(255) not null,
    STATUS integer,
    primary key (AUTH_NAME)
)

Когда язапустите следующий код:

    @Test
public void createMobileTan() {
    MobileTan tan = createMobileTan();
    session.flush();
    System.err.println("Tan:  " + tan.getId());
    System.err.println("Authenticator:  " + tan.getAuthenticator().getId());
    System.err.println("AUTH COLUMN:  " + session.createSQLQuery("select AUTH_NAME from mobile_tan").addScalar("AUTH_NAME", IntegerType.INSTANCE).setMaxResults(1).uniqueResult());
    System.err.println("AUTH COLUMN:  " + session.createSQLQuery("select ID from mobile_tan").addScalar("ID", IntegerType.INSTANCE).setMaxResults(1).uniqueResult());
}

public MobileTan createMobileTan() {
    Authenticator auth = createAuthenticator();

    MobileTan tan = new MobileTan();

    tan.setAuthenticator(auth);
    tan.setMobilePhoneNbr("0123456");

    session.persist(tan);

    return tan;
}

private Authenticator createAuthenticator() {
    Authenticator auth = new Authenticator();

    session.persist(auth);

    return auth;
}

Я получаю следующий вывод:

Tan:  1
Authenticator:  1
AUTH COLUMN:  1


java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: ID in statement [select ID from mobile_tan limit ?]
    at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
    at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
    at org.hsqldb.jdbc.JDBCPreparedStatement.<init>(Unknown Source)
    at org.hsqldb.jdbc.JDBCConnection.prepareStatement(Unknown Source)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:146)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172)

Таблица создается с использованием первичного ключа, который также является внешним ключом.Как видите, он правильно устанавливает отношения объекта, и я могу получить к нему доступ с помощью метода getId () в классе MobileTan.Но, как вы также можете видеть в определении таблицы DDL, в базе данных физически создается только один столбец.Вам действительно нужно 2 физических столбца?Затем вы можете решить использовать только один столбец, а также использовать этот единственный столбец в качестве внешнего ключа.Затем опустите дополнительный столбец.

Я создал свой пример, используя информацию, включенную в эту ссылку здесь: set-primary-key-and-external-key-on-single-column

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