Остановить Hibernate от обновления существующих записей - PullRequest
4 голосов
/ 23 марта 2012

Я пытаюсь перейти в спящий режим, чтобы писать новые объекты каждый раз, когда выполняю сохранение, а не обновляю существующие записи.

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;
}

Так что, если написать риск в БД. Затем я меняю транспортное средство в Интернете и пытаюсь сохранить риск для БД во второй раз. Я хотел бы иметь два риска в таблице рисков и два транспортных средства в таблице транспортных средств.

В настоящее время я использую сеанс гибернации 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 ]

5 голосов
/ 07 апреля 2012

Я действительно сомневаюсь, что Hibernate может сделать это автоматически.Вы хотите переопределить одно из самых основных поведений ORM.Весь их пакет состоит в том, что они отслеживают ваши объекты в какой-то сессии, чтобы сохранить эти обновления в базе данных без необходимости самостоятельно сортировать эти обновления.

Если вы не хотите, чтобы записи обновлялись, сделайте ваши установщикичастный и аннотировать свои поля, так что вы получите доступ к полю.В 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)
}
4 голосов
/ 07 апреля 2012

Я думаю, что более простой способ сделать это должен быть:

  1. Сохранение риска и транспортного средства за одну сессию.
  2. Сразу после закрытия сеанса установите идентификаторы на ноль.
  3. А затем при необходимости заново сохраните объекты.

Очень важно, чтобы вы закрывали сеанс Hibernate (используйте отдельный сеанс для каждого сохранения), чтобы объекты становились отсоединенными, а когда объекты снова присоединяются, Hibernates думает, что они совершенно новые.

Примечание: вы должны изменить long на Long (примитив для оболочки)

3 голосов
/ 04 апреля 2012

Вероятно, оба экземпляра 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);
2 голосов
/ 11 апреля 2012

Я думаю, вам нужно взглянуть на Hibernate Envers (http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch15.html)

2 голосов
/ 09 апреля 2012

Одним из возможных решений является переопределение sql-обновления для Vehicle с собственным SQL.

Вот пример для пользователя:

<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 длявместо этого вставить?

2 голосов
/ 09 апреля 2012

Я предполагаю, что код, который выполняется в вашем случае, выглядит примерно так:

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 (), и объект Risk, и объект Vehicle изменяются с точки зрения Hibernate, переходя от transient к * 1007 стойкие *. Эти состояния подробно описаны в руководстве Hibernate ORM .

Устанавливая марку на постоянном транспортном средстве, вы прямо заявляете, что хотите редактировать существующую строку в базе данных, а НЕ создавать новую. Если вы хотите создать новый, следуйте указаниям в руководстве по Hibernate и измените второй раздел на:

Vehicle vehicle2 = new Vehicle(otherMake, model, regNumber, abiCode)
risk = new Risk(vehicle2);
session.save(risk);
2 голосов
/ 06 апреля 2012

Это поможет увидеть 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;
}
2 голосов
/ 23 марта 2012

Я предполагаю, что один автомобиль может иметь много рисков, да?

Класс риска и отображение гибернации риска выглядит нормально, но как насчет транспортного средства?1007 * См. Набор рисков (исходя из множества рисков для одного транспортного средства).Кроме того, в вашем автомобильном картографировании должно быть что-то вроде

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

Надеюсь, это поможет и удачи :)

1 голос
/ 10 апреля 2012

Hibernate построен с использованием слушателей, в эко-системе hibernate есть списки для каждого вида деятельности

DefaultSaveOrUpdateEventListener используется для сохранения или обновления объектов с использованием спящего режима, метод onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent) будет содержать информацию об объекте, который сохраняется. когда вы набираете session.save(modelObject), ваш вызов закончится в приемнике, здесь вы можете установить идентификатор на ноль, и система будет добавлять новую запись каждый раз.

вот образец вот сообщение в блоге, детализирующее использование слушателя

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

1 голос
/ 07 апреля 2012

Использовать Session.evict.После удаления объекта он не будет обновляться в базе данных:

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

Таким образом, объект не обновляется в базе данных, если вы явно не вызываете «update» или «merge» (или «saveOrUpdate»)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...