我试图让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>
答案 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)
我认为更简单的方法应该是:
关闭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)。如果需要,可以自动将新实体附加到会话。