Hibernate:嵌入式id字段命名问题

时间:2015-06-06 18:57:03

标签: java hibernate jpa

我有一个实体CashFlow,其实体类category的字段CashFlowCategory。后者有一个由字段nameusername组成的复合键。在数据库中,这些字段称为NAMEUSERNAME

该应用程序在EclipseLink上工作正常,但当我尝试切换到Hibernate时遇到了这个异常:

org.hibernate.HibernateException: Missing column: category_NAME in .APP.CASHFLOW

看起来Hibernate(与EclipseLink不同)在category_之前需要NAME前缀,即使我明确声明该列的名称不带前缀:

@Column(name="NAME")
private String name;

如何告诉Hibernate寻找" NAME"而不是" category_NAME"?

设置:

  • Glassfish 4.0
  • Derby 10.9.1.0

数据库结构:

表现金流:

COLUMN_NAME         |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL&
------------------------------------------------------------------------------
ID                  |INTEGER  |0   |10  |10    |NULL      |NULL      |NO
AMOUNT              |DOUBLE   |NULL|2   |52    |NULL      |NULL      |YES
DATE                |DATE     |0   |10  |10    |NULL      |NULL      |YES
DESCRIPTION         |VARCHAR  |NULL|NULL|255   |NULL      |510       |YES
USERNAME            |VARCHAR  |NULL|NULL|255   |NULL      |510       |YES
NAME                |VARCHAR  |NULL|NULL|255   |NULL      |510       |YES
IDNUMBER            |INTEGER  |0   |10  |10    |NULL      |NULL      |YES

表CASHFLOWCATEGORY

COLUMN_NAME         |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL&
------------------------------------------------------------------------------
TYPE                |INTEGER  |0   |10  |10    |NULL      |NULL      |YES
NAME                |VARCHAR  |NULL|NULL|255   |NULL      |510       |NO
USERNAME            |VARCHAR  |NULL|NULL|255   |NULL      |510       |NO
LIMIT               |DOUBLE   |NULL|2   |52    |NULL      |NULL      |YES
GOAL                |DOUBLE   |NULL|2   |52    |NULL      |NULL      |YES

实体:

现金流

package com.singularityfx.cashelyok.entities;

import java.time.LocalDate;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.singularityfx.cashelyok.entities.enums.CashFlowType;

@Entity
public class CashFlow implements Comparable<CashFlow> {
    @Id
    @GeneratedValue
    private int id;
    @NotNull @Min(0)
    private double amount;
    @Size(max=200)
    private String description;
    @NotNull
    private LocalDate date;
    @OneToOne @NotNull
    private CashFlowCategory category;
    @Transient
    private boolean editable = false;

    public CashFlow() {}

    public CashFlow(
                LocalDate date,
                CashFlowCategory category,
                String description,
                double amount) {
        this.date = date;
        this.category = category;
        this.description = description;
        this.amount = amount;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public CashFlowType getType() {
        return category.getType();
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public LocalDate getDate() {
        return date;
    }

    public void setDate(LocalDate date) {
        this.date = date;
    }

    public CashFlowCategory getCategory() {
        return category;
    }

    public void setCategory(CashFlowCategory category) {
        this.category = category;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Column(name="username")
    public String getUsername() {
        return category.getPrimaryKey().getUsername();
    }

    public boolean isEditable() {
        return editable;
    }

    public void setEditable(boolean editable) {
        this.editable = editable;
    }

    @Override
    public String toString() {
        return "(" + date + "|" + category + "|" + description 
                + "|" + amount + "|" + category.getPrimaryKey().getUsername() + ")";  
    }

    @Override
    public int compareTo(CashFlow o) {
        return this.getDate().compareTo(o.getDate());
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        long temp;
        temp = Double.doubleToLongBits(amount);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        result = prime * result
                + ((category == null) ? 0 : category.hashCode());
        result = prime * result + ((date == null) ? 0 : date.hashCode());
        result = prime * result
                + ((description == null) ? 0 : description.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CashFlow other = (CashFlow) obj;
        if (Double.doubleToLongBits(amount) != Double
                .doubleToLongBits(other.amount))
            return false;
        if (category == null) {
            if (other.category != null)
                return false;
        } else if (!category.equals(other.category))
            return false;
        if (date == null) {
            if (other.date != null)
                return false;
        } else if (!date.equals(other.date))
            return false;
        if (description == null) {
            if (other.description != null)
                return false;
        } else if (!description.equals(other.description))
            return false;
        return true;
    }
}

CashFlowCategory

package com.singularityfx.cashelyok.entities;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Transient;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

import com.singularityfx.cashelyok.entities.enums.CashFlowType;

@Entity
public class CashFlowCategory implements Comparable<CashFlowCategory> {
    @EmbeddedId
    private CashFlowCategoryPK primaryKey;
    @NotNull
    private CashFlowType type;
    @Min(0)
    private double goal;
    @Transient
    public static final String SEPARATOR = " - ";

    public CashFlowCategory() {}

    public CashFlowCategory(String name, CashFlowType type, double goal, String username) {
        this.primaryKey = new CashFlowCategoryPK(name, username);
        this.type = type;
        this.goal = goal;
    }

    @Override
    public String toString() {
        return type + SEPARATOR + primaryKey.getName();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((primaryKey == null) ? 0 : primaryKey.hashCode());
        result = prime * result + ((type == null) ? 0 : type.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CashFlowCategory other = (CashFlowCategory) obj;
        if (primaryKey == null) {
            if (other.primaryKey != null)
                return false;
        } else if (!primaryKey.equals(other.primaryKey))
            return false;
        if (type != other.type)
            return false;
        return true;
    }

    @Override
    public int compareTo(CashFlowCategory o) {
        /*
         * To avoid NullPointerException from javax.faces.component.UIInput.compareValues
         * when adding new cash flow via cash flows page after attempting to add
         * cash flow without filling all mandatory fields
         */
        if (type == null) {  
            return -1; 
        }

        int returnValue = type.compareTo(o.getType());
        if (returnValue == 0) {
            returnValue = primaryKey.getName().compareTo(o.getName());
        }
        return returnValue;
    }   

    public CashFlowCategoryPK getPrimaryKey() {
        return primaryKey;
    }

    public void setPrimaryKey(CashFlowCategoryPK primaryKey) {
        this.primaryKey = primaryKey;
    }

    public String getName() {
        return primaryKey.getName();
    }

    public CashFlowType getType() {
        return type;
    }

    public String getUsername() {
        return primaryKey.getUsername();
    }

    public double getGoal() {
        return goal;
    }

    public void setGoal(double goal) {
        this.goal = goal;
    }   
}

CashFlowCategoryPK(嵌入式ID)

package com.singularityfx.cashelyok.entities;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Embeddable
public class CashFlowCategoryPK implements Serializable {
    private static final long serialVersionUID = 392533037882395947L;
    @NotNull @Size(min=1, max=200)
    @Column(name="NAME")
    private String name;
    @NotNull @Size(min=1, max=200)
    @Column(name="USERNAME")
    private String username;

    public CashFlowCategoryPK() {   
    }

    public CashFlowCategoryPK(String name, String username) {
        this.name = name;
        this.username = username;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result
                + ((username == null) ? 0 : username.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CashFlowCategoryPK other = (CashFlowCategoryPK) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (username == null) {
            if (other.username != null)
                return false;
        } else if (!username.equals(other.username))
            return false;
        return true;

    }
    public String getName() {
        return name;
    }

    public String getUsername() {
        return username;
    }
}

堆栈跟踪:

org.hibernate.HibernateException: Missing column: category_NAME in .APP.CASHFLOW
    at org.hibernate.mapping.Table.validateColumns(Table.java:366)
    at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1338)
    at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:175)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:525)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
    at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
    at org.hibernate.jpa.HibernatePersistenceProvider.createContainerEntityManagerFactory(HibernatePersistenceProvider.java:152)
    at org.glassfish.persistence.jpa.PersistenceUnitLoader.loadPU(PersistenceUnitLoader.java:199)
    at org.glassfish.persistence.jpa.PersistenceUnitLoader.<init>(PersistenceUnitLoader.java:107)
    at org.glassfish.persistence.jpa.JPADeployer$1.visitPUD(JPADeployer.java:223)
    at org.glassfish.persistence.jpa.JPADeployer$PersistenceUnitDescriptorIterator.iteratePUDs(JPADeployer.java:510)
    at org.glassfish.persistence.jpa.JPADeployer.createEMFs(JPADeployer.java:230)
    at org.glassfish.persistence.jpa.JPADeployer.prepare(JPADeployer.java:168)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.prepareModule(ApplicationLifecycle.java:922)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:431)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
    at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:527)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:523)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:360)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:522)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:546)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1423)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1500(CommandRunnerImpl.java:108)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1762)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1674)
    at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:534)
    at com.sun.enterprise.v3.admin.AdminAdapter.onMissingResource(AdminAdapter.java:224)
    at org.glassfish.grizzly.http.server.StaticHttpHandler.service(StaticHttpHandler.java:297)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:246)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:745)]]

的persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 
xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="cashelyok" transaction-type="JTA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <jta-data-source>cashelyok-resource</jta-data-source>
    <properties>
      <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" />
      <property name="hibernate.hbm2ddl.auto" value="validate" />
    </properties>
  </persistence-unit>
</persistence>

1 个答案:

答案 0 :(得分:1)

正如您所说,您明确告诉Hibernate将CashFlowCategoryPK的{​​{1}}字段映射到name列;但错误实际上是关于Hibernate在NAME的表category_NAME中寻找连接(外键)列CashFlow。这是因为您未在.APP.CASHFLOW的{​​{1}}字段中指定加入列。

根据JPA规范,默认的连接键列名称采用CashFlow形式(在您的情况下,将为category;因此出现错误消息)。我猜EclipseLink执行的默认值比规范要求的要多。要解决此问题,您需要在<ref_field_name>_<pk_column_name>中明确指定连接列:

category_NAME