jpa - 将 JPA 实体继承与涉及对象引用的 IdClass 复合主键结合使用

标签 jpa composite-primary-key object-reference

我正在尝试使用 IdClass 复合主键,给出以下场景:

  • 主键涉及对象引用(实体对象)
  • 主键中的实体对象是其他实体的外键(具有 OneToOne 或 ManyToOne 映射)
  • 具有复合 Id 的实体类具有子类

下面是相关的实体类:

契约(Contract).java

@Entity
@IdClass(ContractPK.class)
@Table(name = "CONTRACT")
public abstract class Contract implements Serializable {
     private FiscalArrangement fiscalArrangement;
     private CrudeType crudeType;

     public Contract() {}

     public Contract(FiscalArrangement fiscalArrangement, CrudeType crudeType) {
        this.fiscalArrangement = fiscalArrangement;
        this.crudeType = crudeType;
     }

     @Id
     @ManyToOne
     public FiscalArrangement getFiscalArrangement() {
          return fiscalArrangement;
     }

     public void setFiscalArrangement(FiscalArrangement fiscalArrangement) {
         this.fiscalArrangement = fiscalArrangement;
     }

     @Id
     @ManyToOne
     public CrudeType getCrudeType() {
         return crudeType;
     }

     public void setCrudeType(CrudeType crudeType) {
         this.crudeType = crudeType;
     }
    //equals and hasCode
}

合约PK.java

public class ContractPK implements Serializable {
    private FiscalArrangement fiscalArrangement;
    private CrudeType crudeType;

    public ContractPK() {}

    public ContractPK(FiscalArrangement fiscalArrangement, CrudeType crudeType) {
        this.fiscalArrangement = fiscalArrangement;
        this.crudeType = crudeType;
    }
  //getters and setters
}

RegularContract.java

@Entity
@DiscriminatorValue("REG")
public class RegularContract extends Contract{}

AlternativeFundingContract.java

@Entity
@DiscriminatorValue("AF")
public abstract class AlternativeFundingContract extends Contract{   
    private Double sharedOilRatio;
    private Double terminalPeriod;
    private Double terminalSharedOil;
    //getters and setters
}

CarryContract.java

@Entity
@DiscriminatorValue("CA")
public class CarryContract extends AlternativeFundingContract {}

ModifiedCarryContract.java

@Entity
@DiscriminatorValue("MCA")
public class ModifiedCarryContract extends AlternativeFundingContract{}

预测.java

@Entity
@IdClass(ForecastPK.class)
@Table(name = "FORECAST")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "FTYPE")
public abstract class Forecast implements Serializable {
    private Integer periodYear;
    private Integer periodMonth;
    private Contract contract;
    //other non-primary properties       

    public Forecast() {}

    @Id
    public Integer getPeriodYear() {
        return periodYear;
    }

    public void setPeriodYear(Integer periodYear) {
        this.periodYear = periodYear;
    }

    @Id
    public Integer getPeriodMonth() {
        return periodMonth;
    }

    public void setPeriodMonth(Integer periodMonth) {
        this.periodMonth = periodMonth;
    }

    @Id
    @ManyToOne
    public Contract getContract() {
        return contract;
    }

    public void setContract(Contract contract) {
       this.contract = contract;
    }

    //other getters and setters   
}

预测PK.java

public class ForecastPK implements Serializable {
    private Integer periodYear;
    private Integer periodMonth;
    private Contract contract;

    //getters and setters
}

RegularForecast.java

@Entity
@DiscriminatorValue("REG")
public class RegularForecast extends Forecast {}

AlternativeFundingForecast.java

Entity
@DiscriminatorValue("AF")
public abstract class AlternativeFundingForecast extends Forecast {
   //properties 
}

CarryForecast.java

@Entity
@DiscriminatorValue("CA")
public class CarryForecast extends AlternativeFundingForecast{}

ModifiedCarryForecast.java

@Entity
@DiscriminatorValue("MCA")
public class ModifiedCarryForecast extends AlternativeFundingForecast{}

Controller 类:JvForecastController.java

@Named(value = "jvProdController")
@SessionScoped
public class JvForecastController implements Serializable {
    @Inject
    private JvForecastServices forecastBean;     
    private Forecast currentProduction;   
    private Integer periodYear;
    private Integer periodMonth;
    private FiscalArrangement currentFiscalArrangement;
    private Contract currentContract;

    public JvForecastServices getForecastBean(){
        return forecastBean;
    }

    public void currentContractChanged() throws Exception {
        if (currentContract instanceof RegularContract) {
            currentProduction = new RegularForecast();
        } else if (currentContract instanceof CarryContract) {            
            currentProduction = new CarryForecast();
        } else if (currentContract instanceof ModifiedCarryContract) {
            currentProduction = new ModifiedCarryForecast();
        } else {
            throw new Exception("Undefined contract type");            
        } 

        //set primary key fields
        currentProduction.setPeriodYear(periodYear);
        currentProduction.setPeriodMonth(periodMonth);
        currentProduction.setContract(currentContract);
    }

    public void productionVolumeChanged() {
        getForecastBean().enrich(currentProduction);        
    }

}

Bean实现类:JvForecastServicesImpl.java

@Dependent
public abstract class JvForecastServicesImpl<T extends Forecast> extends    CommonServicesImpl<T> implements JvForecastServices<T>, Serializable {
    public JvForecastServicesImpl(Class<T> entityClass) {
        super(entityClass);
    }

    @Override
    public T computeOpeningStock(T forecast) {
        Forecast prod = getPreviousMonthProduction(forecast);
        //some code
        return forecast;
    }

    @Override
    public T getPreviousMonthProduction(T forecast) {
        int month = forecast.getPeriodMonth();
        int year = forecast.getPeriodYear();
        Contract cs = forecast.getContract();
        Contract contract = null;

        if (cs instanceof RegularContract) {
             contract = new RegularContract(cs.getFiscalArrangement(),    cs.getCrudeType());
        } else if (cs instanceof CarryContract) {
            contract = new CarryContract(cs.getFiscalArrangement(), cs.getCrudeType());
        } else if (contract instanceof ModifiedCarryContract) {
            contract = new ModifiedCarryContract(cs.getFiscalArrangement(), cs.getCrudeType());
        }

        FiscalPeriod prevFp = getPreviousFiscalPeriod(year, month);

        T f = find(new ForecastPK(prevFp.getYear(), prevFp.getMonth(), contract));

        return f;
    }

}

CommonServicesImpl.java

public abstract class CommonServicesImpl<T> extends AbstractCrudServicesImpl<T> implements CommonServices<T> {}

AbstractCrudServicesImpl.java中find()方法的实现

public abstract class AbstractCrudServicesImpl<T> implements AbstractCrudServices<T> {
protected final Class<T> entityClass;
    public AbstractCrudServicesImpl(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    protected abstract EntityManager getEntityManager();

    //some CRUD methods  

    @Override
    public T find(Object id) {
        return getEntityManager().find(entityClass, id);
    }
}

当 JvForecastServicesImpl 类中的以下代码行执行时:

T f = find(new ForecastPK(prevFp.getYear(), prevFp.getMonth(), contract));

我收到错误信息:

10:24:59,492 INFO [org.hibernate.event.internal.DefaultLoadEventListener] (default task-42) HHH000327: Error performing load command : org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.nnpcgroup.cosm.entity.contract.Contract 10:24:59,497 ERROR [org.jboss.as.ejb3.invocation] (default task-42) WFLYEJB0034: EJB Invocation failed on component RegularForecastBean for method public abstract java.lang.Object com.nnpcgroup.cosm.ejb.CommonServices.enrich(java.lang.Object): javax.ejb.EJBException: java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.nnpcgroup.cosm.entity.contract.Contract at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleExceptionInOurTx(CMTTxInterceptor.java:187) at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:277) at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:327) at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:239) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:43) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:100) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:66) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:64) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:356) at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:636) at org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:61) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:356) at org.jboss.invocation.PrivilegedWithCombinerInterceptor.processInvocation(PrivilegedWithCombinerInterceptor.java:80) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:195) at org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:185) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73) at com.nnpcgroup.cosm.ejb.forecast.jv.JvRegularForecastServices$$$view23.enrich(Unknown Source) at com.nnpcgroup.cosm.controller.JvForecastController.productionVolumeChanged(JvForecastController.java:233) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.sun.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:181) at com.sun.el.parser.AstValue.invoke(AstValue.java:289) at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:304) at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40) at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50) at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40) at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50) at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105) at com.sun.faces.facelets.tag.jsf.core.AjaxBehaviorListenerImpl.processAjaxBehavior(AjaxHandler.java:459) at javax.faces.event.AjaxBehaviorEvent.processListener(AjaxBehaviorEvent.java:113) at javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:106) at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:805) at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790) at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282) at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:658) at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131) at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) at io.undertow.server.handlers.DisableCacheHandler.handleRequest(DisableCacheHandler.java:33) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:51) at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) at io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:56) at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50) at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:284) at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:263) at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81) at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:174) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:793) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.nnpcgroup.cosm.entity.contract.Contract at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1689) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1619) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1106) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1033) at org.jboss.as.jpa.container.AbstractEntityManager.find(AbstractEntityManager.java:213) at com.nnpcgroup.cosm.ejb.impl.AbstractCrudServicesImpl.find(AbstractCrudServicesImpl.java:44) at com.nnpcgroup.cosm.ejb.forecast.jv.impl.JvForecastServicesImpl.getPreviousMonthProduction(JvForecastServicesImpl.java:204) at com.nnpcgroup.cosm.ejb.forecast.jv.impl.JvForecastServicesImpl.computeOpeningStock(JvForecastServicesImpl.java:47) at com.nnpcgroup.cosm.ejb.forecast.jv.impl.JvForecastServicesImpl.enrich(JvForecastServicesImpl.java:112) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.jboss.as.ee.component.ManagedReferenceMethodInterceptor.processInvocation(ManagedReferenceMethodInterceptor.java:52) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:437) at org.jboss.as.weld.ejb.Jsr299BindingsInterceptor.doMethodInterception(Jsr299BindingsInterceptor.java:82) at org.jboss.as.weld.ejb.Jsr299BindingsInterceptor.processInvocation(Jsr299BindingsInterceptor.java:93) at org.jboss.as.ee.component.interceptors.UserInterceptorFactory$1.processInvocation(UserInterceptorFactory.java:63) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.invocationmetrics.ExecutionTimeInterceptor.processInvocation(ExecutionTimeInterceptor.java:43) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.jpa.interceptor.SBInvocationInterceptor.processInvocation(SBInvocationInterceptor.java:47) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:437) at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:64) at org.jboss.as.weld.ejb.EjbRequestScopeActivationInterceptor.processInvocation(EjbRequestScopeActivationInterceptor.java:83) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:21) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) at org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.component.pool.PooledInstanceInterceptor.processInvocation(PooledInstanceInterceptor.java:51) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340) at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:275) ... 86 more

最佳答案

JPA 将这种类型的复合主键称为“派生标识”。

您需要更改您的 IdClasses。 ContractPK 应该看起来像这样(假设 FiscalArrangementCrudeType 有简单的主键):

public class ContractPK implements Serializable {
    private Integer fiscalArrangement;
    private Integer crudeType;
    ...
}

请注意,属性名称与 Entity@Id 属性名称匹配,但类型必须与目标 Entity 的类型匹配' 主键。

如果目标 Entity 使用 @IdClasses,ContractPK 应该如下所示:

public class ContractPK implements Serializable {
    private FiscalArrangementPK fiscalArrangement;
    private CrudeTypePK crudeType;
    ...
}

注意属性类型现在是 IdClasses。

同样,ForecastPK 应该看起来像这样:

public class ForecastPK implements Serializable {
    private Integer periodYear;
    private Integer periodMonth;
    private Integer contract;
    ...
}

或者这个:

public class ForecastPK implements Serializable {
    private Integer periodYear;
    private Integer periodMonth;
    private ContractPK contract;
    ...
}

派生身份在 JPA 2.1 规范的第 2.4.1 节中进行了讨论。

关于jpa - 将 JPA 实体继承与涉及对象引用的 IdClass 复合主键结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37744897/

相关文章:

java - 将子实体持久化操作级联到其父实体

MySQL 两张表带键的设计

c++ - 当 const ref 被原始对象(非常量)替换时,const-ness 会消失吗?

java - JPA:AttributeConverter 的参数化实例

sql - WHERE col1,col2 IN (...) [使用复合主键的 SQL 子查询]

spring - 有没有一种方法可以基于许多数据库表创建一个 JPA 实体,我真的必须这样做还是这是一种不好的做法?

objective-c - 如何为对象指针指针(id *)选择 ARC 桥接说明符?

C++ 创建对象的全局引用

java - 验证通过,但是当调用 merge() 时,会抛出验证错误

hibernate - JPA 条件查询,上课顺序