我有一个实体CashFlow
,其实体类category
的字段CashFlowCategory
。后者有一个由字段name
和username
组成的复合键。在数据库中,这些字段称为NAME
和USERNAME
。
该应用程序在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"?
设置:
数据库结构:
表现金流:
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>
答案 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