阻止Hibernate更新现有记录

时间:2012-03-23 10:47:15

标签: java sql hibernate java-ee

我试图让hibernate在每次执行保存时写入新对象,而不是更新现有记录。

public class Risk {
    private long riskID;
    private Vehicle vehicle;
}

public class Vehicle {  
    private long vehicleID;
    private long riskID;
    private String regNumber;
    private String abiCode;
    private String make;
    private String model;
}

所以如果把风险写入DB。然后我在网上更换车辆,并尝试再次将风险保存到数据库。我想在风险表中有两个风险,在车辆表中有两个车辆。

目前我正在使用hibernate会话Save(Object o)。这总是在数据库中造成新风险,但从未创建新车辆。它只是更新原始的。

这是我的映射文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class lazy="false" name="uk.co.test.Risk" schema="quote_engine" table="GV_RISK" >
    <id column="riskID" name="riskID" type="long">
        <generator class="identity"/>
    </id>

    <many-to-one name="vehicle" class="uk.co.test.Vehicle" column="vehicleID" not-null="true" cascade="all" unique="true" />

</class>

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="uk.co.test.Vehicle" schema="quote_engine" table="GV_VEHICLE">

    <id name="vehicleID" type="long" column="vehicleID">
        <generator class="identity" />
    </id>

    <property name="regNumber" type="string" column="regNumber" />
    <property name="abiCode" type="string" column="abiCode" />
    <property name="make" type="string" column="make" />
    <property name="model" type="string" column="model" />


</class>

12 个答案:

答案 0 :(得分:5)

我真的怀疑是否可以让Hibernate自动神奇地做到这一点。您想要覆盖ORM的一个最基本的行为。他们的整个包就是在一些Session中跟踪你的对象,以便将这些更新保存到数据库,而无需你自己对这些更新进行排序。

如果您不想更新记录,请将您的setter设为私有并注释您的字段,以便获得字段访问权限。在xml中,在default-access="field"标记上设置<hibernate-mapping>属性。

然后,您需要先创建一个新对象,然后再将其添加到会话中。使用如下所示的方法获得服务:

public void saveVehicle(Vehicle vehicle) {
  Vehicle vehicleNew = vehicle.copy();
  // Risk would be copied via risk.copy() in vehicle.copy()

  vehicleDao.save(vehicleNew)
}

答案 1 :(得分:4)

我认为更简单的方法应该是:

  1. 在一个会话中保存风险和车辆。
  2. 关闭会话后,将ID设置为null。
  3. 然后,在需要时,重新保存对象。
  4. 关闭Hibernate会话(每次保存使用单独的会话)非常重要,这样对象就会分离,当对象重新附加时,Hibernates认为它们是全新的。

    注意:你应该为Long更改long(原语到包装器)

答案 2 :(得分:3)

两个Risk实例可能在多对一关系中具有相同的Vehicle实例。如果您修改此Vehicle实例,则可以为两个Risk实例修改它,而不是创建新实例。

您必须驱逐Vehicle实例,或者必须保存克隆实例。

我给出了两种可能性的想法(不是工作代码)。假设Risk实例是r1和r2。

1)随着逐出:

Risk r2 = r1.clone(); // or whatever you do to create your second risk instance

session.evict(r2.getVehicle());
session.save(r2.getVehicle()); // session.save(r2) also should work.

(注意:在这种情况下,r1也指r2的车辆实例。如果你不再使用r1实例,那么它可以工作。如果你还想使用r1实例,你必须驱逐它并从数据库重新加载。)

2)使用克隆:

Risk r2 = r1.clone(); // or whatever you do to create your second risk instance

Vehicle v2 = r1.getVehicle.clone(); // create a new instance!
r2.setVehicle(v2);
session.save(r2);

答案 3 :(得分:2)

我认为一辆车可能有很多风险吗?

风险等级和风险hibernate映射看起来不错,但车辆呢?

课程应该是:

public class Vehicle
{  
    private long vehicleID;
    private Set<Risk> risks;
    private String regNumber;
    private String abiCode;
    private String make;
    private String model;
}

查看风险集(来自一个车辆关系的许多风险)。此外,在您的车辆映射中,您应该有类似

的内容
<set name="risks" table="GV_RISK">
    <key column="riskID" />            
    <one-to-many class="Risk"/>
</set> 

希望这会有所帮助并祝你好运:)

答案 4 :(得分:2)

查看每个表的DDL会有所帮助。在使用唯一外键关联时,可以使用“多对一”映射进行“一对一”关联。如果他们共享相同的主键,您可以一对一使用。

您可以尝试在车辆映射中以一对一的方式进行双向映射,但这不是必需的。这是它的外观。

<one-to-one name="risk" class="uk.co.test.Risk" property-ref="riskID"/> 

然而,你要做的事情非常重要。我可能会稍微更改类定义并仔细检查您的DDL。

例如(保持hbm映射完全按照上面定义的那样):

表结构如下:

create table GV_VEHICLE (
    vehicleID int(11) not null auto_increment,
    regNumber varchar(50) not null,
    abiCode varchar(50) not null,
    make varchar(50) not null,
    model varchar(50) not null,
    primary key(vehicleID)));

create table GV_RISK (
    riskID int(11) not null auto_increment,
    vehicleID int(11),
    primary key(riskID),
    foreign key(vehicleID) references GV_VEHICLE(vehicleID));

创建一对一的类定义,每个类定义具有相互的反射

public class Vehicle {  
    private long vehicleID;
    private String regNumber;
    private String abiCode;
    private String make;
    private String model;
    private Risk risk;
}

public class Risk {
    private long riskID;
    private Vehicle vehicle;
}

答案 5 :(得分:2)

我将假设在您的情况下执行的代码类似于:

vehicle = new Vehicle(make, model, regNumber, abiCode)
risk = new Risk(vehicle);
session.save(risk);

vehicle.setMake(otherMake)
risk = new Risk(vehicle)
session.save(risk);

你遇到的问题是,只要你调用第一个session.save(),在Hibernate的观点中,Risk和Vehicle对象都会改变状态,从 transient 转移到持久。这些状态详见Hibernate ORM manual

通过在持久性车辆上设置make,您明确表示要编辑数据库中的现有行,而不是要创建新行。如果要创建新的,请按照Hibernate手册中的信息,将第二节更改为:

Vehicle vehicle2 = new Vehicle(otherMake, model, regNumber, abiCode)
risk = new Risk(vehicle2);
session.save(risk);

答案 6 :(得分:2)

其中一种可能的解决方案是使用本机SQL覆盖Vehicle的sql-update。

这是人的例子:

<class name="Person">
<id name="id">
    <generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>

所以你可以覆盖sql-uptade来代替插入吗?

答案 7 :(得分:2)

我认为你需要看看Hibernate Envers(http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch15.html)

答案 8 :(得分:1)

This Statment Will Help You

SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.saveOrUpdate(object);
transaction.commit();

答案 9 :(得分:1)

使用Session.evict。在驱逐对象后,它将不会在数据库中更新:

Entity entity = ....
session.evict(entity);
entity.setA(a); // won't affect the database

因此,除非您明确调用“update”或“merge”(或“saveOrUpdate”),否则数据库中的对象不会更新。

答案 10 :(得分:1)

Hibernate是使用监听器构建的,hibernate生态系统中的每个活动都有列表

DefaultSaveOrUpdateEventListener用于使用hibernate保存或更新对象, onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent)方法将包含有关正在保存的对象的信息。 当你拨打session.save(modelObject)时,你的电话会在听众中结束,你可以在这里将ID设置为空,系统每次都会添加一条新记录。

here is a sample here is a blog post detailing the use of the listener

<property name="eventListeners"> <map> <entry key="save-update"><ref local="saveUpdateListener" /></entry> </map> </property>

答案 11 :(得分:0)

了解项目的上下文以及您最终要实现的目标会很有用。

你问题的解决方案可能在于你所要求的技术诀窍。

我怀疑您想以某种方式跟踪变化,即维持历史审计线索。对于hibernate,您可以使用Envers执行此操作。它提供@Audited注释,使您可以查询历史数据。

否则,您可能需要重新考虑不变性的类。任何实体都不应该是可变的。实体中的任何更改都会产生一个新实体(在功能世界中有时称为persistent data structure)。如果需要,可以自动将新实体附加到会话。