java - Hibernate 合并正在生成新行而不是更新
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31379111/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java - Hibernate merge is generating new rows instead of updating
提问by EXSolo
I am using Hibernate-EntityManager version 4.3.5
. I have multiple tables using an id with a generated value.
我正在使用Hibernate-EntityManager version 4.3.5
. 我有多个表使用带有生成值的 id。
Inserting and such works fine, but when I try to merge Hibernate does not update the old rows and creates new ones.
插入等工作正常,但是当我尝试合并 Hibernate 时,不会更新旧行并创建新行。
The content is the same, but the id is incrementing.
内容是一样的,但是id是递增的。
Before merge:
合并前:
After merge:
合并后:
I tried to use a custom Id generator to check if the Id already exists. With this new strategy i get the following error:
我尝试使用自定义 Id 生成器来检查 Id 是否已存在。使用这个新策略,我收到以下错误:
Caused by: java.sql.SQLException: Field 'product_id' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1094)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4226)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4158)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2615)
EDIT:
编辑:
I found out that @GeneratedValues will not set the ids in the local variables. That means that you might override the existing ones while merging. Though, that did not solve my problem.
我发现 @GeneratedValues 不会在局部变量中设置 ID。这意味着您可能会在合并时覆盖现有的。虽然,这并没有解决我的问题。
Here is the code I am using:
这是我正在使用的代码:
@Entity(name = "calculation_storage")
@Proxy(lazy = false)
public class CalculationStorage {
@Id
private Date created;
@OneToMany(mappedBy = "storage", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Product> products;
public CalculationStorage(Date created) {
this.created = created;
this.products = new LinkedList<Product>() {
@Override
public boolean add(Product product) {
product.setStorage(CalculationStorage.this);
return super.add(product);
}
};
}
public CalculationStorage(Date created, List<Product> products) {
this.created = created;
this.products = products;
}
public CalculationStorage() {
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
/**
* Durch das aufrufen get add Methode wird der benoetigte Fremdschluesseleintrag gesetzt
*
* @return Alle Produkte die registriert sind
*/
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> entities) {
this.products = entities;
}
Produkt:
产品:
@Entity(name = "product")
public class Product implements Serializable {
@Id
@Column(name = "product_id")
//@GeneratedValue
@GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
@GenericGenerator(name="IdOrGenerated",
strategy="at.iktopia.firmenverwaltung.abendabrechnung.control.hibernate.ProductIdGenerator"
)
private int id;
private double costs;
private String name;
private ProductType type;
@OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<CalculatorEntity> entities;
@ManyToOne
@JoinColumn(name = "product_fk", nullable = false)
private CalculationStorage storage;
public Product(String name, ProductType type, double costs) {
this.name = name;
this.type = type;
this.costs = costs;
this.entities = new LinkedList<>();
}
/**
* JPA - Konstruktor
*/
public Product() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductType getType() {
return type;
}
public void setType(ProductType type) {
this.type = type;
}
public List<CalculatorEntity> getEntities() {
return entities;
}
public void setEntities(List<CalculatorEntity> entities) {
this.entities = entities;
}
public double getCosts() {
return costs;
}
public void setCosts(double costs) {
this.costs = costs;
}
public CalculationStorage getStorage() {
return storage;
}
public void setStorage(CalculationStorage calculationStorage) {
this.storage = calculationStorage;
}
}
The Generator:
发电机:
public class ProductIdGenerator extends IdentityGenerator {
@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
if (obj == null) throw new HibernateException(new NullPointerException());
if ((((Product) obj).getId()) == 0) return super.generate(session, obj);
else return ((Product) obj).getId();
}
}
The update method that is called to merge the object:
为合并对象而调用的更新方法:
public class CalculationManager implements Manager<CalculationStorage, Date, CalculationStorageList> {
/**
* @see Manager#save(Object)
*/
@Override
public void save(CalculationStorage calculationStorage) {
Validate.notNull(calculationStorage);
EntityManager em = PersistenceManager.getInstance().getEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
em.persist(calculationStorage);
transaction.commit();
} catch (Exception e) {
ErrorHandler.handleException(e, JPA_PERSIST_ERROR_MESSAGE);
if (transaction.isActive())
transaction.rollback();
}
em.close();
}
/**
* @see Manager#update(Object)
*/
@Override
public void update(CalculationStorage calculations) {
Validate.notNull(calculations);
EntityManager em = PersistenceManager.getInstance().getEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
CalculationStorage c = em.getReference(CalculationStorage.class, calculations.getCreated());
c.setProducts(calculations.getProducts());
em.merge(c);
transaction.commit();
} catch (EntityNotFoundException e) {
if (transaction.isActive())
transaction.rollback();
save(calculations);
} catch (Exception e) {
ErrorHandler.handleException(e, JPA_PERSIST_ERROR_MESSAGE);
if (transaction.isActive())
transaction.rollback();
}
em.close();
}
/**
* @see Manager#getAll()
*/
@Override
public CalculationStorageList getAll() {
EntityManager em = PersistenceManager.getInstance().getEntityManager();
List<CalculationStorage> calculations;
try {
calculations = em.createQuery("SELECT c FROM calculation_storage c").getResultList();
} catch (Exception e) {
ErrorHandler.handleException(e, JPA_LOAD_ERROR_MESSAGE);
return null;
}
em.close();
return new CalculationStorageList(calculations);
}
/**
* @see Manager#remove(Object)
*/
@Override
public void remove(Date primaryKey) {
Validate.notNull(primaryKey);
EntityManager em = PersistenceManager.getInstance().getEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
em.remove(em.getReference(CalculationStorage.class, primaryKey));
transaction.commit();
} catch (Exception e) {
ErrorHandler.handleException(e, JPA_PERSIST_ERROR_MESSAGE);
if (transaction.isActive())
transaction.rollback();
}
em.close();
}
/**
* @see Manager#get(Object)
*/
@Override
public CalculationStorage get(Date primaryKey) {
Validate.notNull(primaryKey);
EntityManager em = PersistenceManager.getInstance().getEntityManager();
try {
CalculationStorage storage = em.getReference(CalculationStorage.class, primaryKey);
em.close();
return storage;
} catch (Exception e) {
ErrorHandler.handleException(e, JPA_LOAD_ERROR_MESSAGE);
return null;
}
}
}
I am clueless what could cause this issue.
我不知道是什么导致了这个问题。
回答by EXSolo
Alright, I found the solution. Thanks a lot to Kulbhushan Singh who made me find out about this:
好的,我找到了解决方案。非常感谢 Kulbhushan Singh,他让我知道了这一点:
I am using @GeneratedValue
in my code:
我@GeneratedValue
在我的代码中使用:
@Id
@Column(name = "product_id")
@GeneratedValue
private int id;
What I didn't know is that when using @GeneratedValue
only the database entries get a ID. The local objects will have the same ID as before. (0 by default). In my case that would mean that product_id
in the database was 1, 2, 3, ... and in my objects it was 0.
我不知道的是,当@GeneratedValue
仅使用数据库条目时会获得一个 ID。本地对象将具有与以前相同的 ID。(默认为 0)。在我的情况下,这意味着product_id
在数据库中是 1、2、3,......而在我的对象中它是 0。
When I tried to update my object I did the following:
当我尝试更新我的对象时,我执行了以下操作:
CalculationStorage dbReference = em.getReference(CalculationStorage.class, calculations.getCreated());
dbReference.setProducts(localStorageObject.getProducts());
I basically set the generated Ids from the database back to 0, because the Id was still 0 in my local object.
我基本上将数据库中生成的 Id 设置回 0,因为 Id 在我的本地对象中仍然是 0。
And because of me setting the Id back to 0, the database generated new Ids.
由于我将 Id 设置回 0,数据库生成了新的 Id。
EDIT: Obviously, if you restart your application the local id values will be 0 again. So try to pull the data from the db when you need it and let the Garbage Collector have it when you are done.
编辑:显然,如果您重新启动应用程序,本地 id 值将再次为 0。因此,请尝试在需要时从数据库中提取数据,并在完成后让垃圾收集器拥有它。