По сути, у меня есть несколько объектов в этой конфигурации (реальная модель данных немного сложнее):
- A имеет отношение "многие ко многим" с B. (B имеет
inverse="true"
) - B имеет отношения "многие к одному" с C. (я
cascade
установил"save-update"
) - C - это своего рода таблица типов / категорий.
Также, вероятно, следует упомянуть, что первичные ключи генерируются базой данных при сохранении.
С моими данными я иногда сталкиваюсь с проблемами, когда у A есть набор разных объектов B, и эти объекты B относятся к одному и тому же объекту C.
Когда я звоню session.saveOrUpdate(myAObject)
, я получаю ошибку спящего режима , говоря: "a different object with the same identifier value was already associated with the session: C"
. Я знаю, что спящий режим не может вставить / обновить / удалить один и тот же объект дважды в одном сеансе, но есть ли способ обойти это? Это не похоже на такую необычную ситуацию.
Во время моего исследования этой проблемы я видел, как люди предлагали использовать session.merge()
, но когда я это делаю, любые «конфликтующие» объекты вставляются в базу данных как пустые объекты со всеми значениями, установленными на null. Ясно, что это не то, что мы хотим.
[Edit] Еще я забыл упомянуть, что (по не зависящим от меня причинам архитектуры) каждое чтение или запись должно выполняться в отдельном сеансе.
Ответы:
Скорее всего, это потому, что объекты B не относятся к одному и тому же экземпляру объекта Java C. Они относятся к одной и той же строке в базе данных (то есть к одному и тому же первичному ключу), но представляют собой разные ее копии.
Итак, что происходит, так это то, что сеанс Hibernate, который управляет объектами, будет отслеживать, какой объект Java соответствует строке с тем же первичным ключом.
Один из вариантов - убедиться, что сущности объектов B, которые ссылаются на одну и ту же строку, фактически ссылаются на один и тот же экземпляр объекта C. В качестве альтернативы отключите каскадирование для этой переменной-члена. Таким образом, когда B сохраняется, C нет. Однако вам придется сохранить C вручную отдельно. Если C - это таблица типов / категорий, тогда, вероятно, имеет смысл быть таким.
источник
Просто установите каскад на MERGE, это должно помочь.
источник
Вам нужно сделать только одно. Запустите,
session_object.clear()
а затем сохраните новый объект. Это очистит сеанс (как правильно названо) и удалит оскорбительный повторяющийся объект из вашего сеанса.источник
Я согласен с @Hemant Kumar, большое спасибо. По его решению я решил свою проблему.
Например:
@Test public void testSavePerson() { try (Session session = sessionFactory.openSession()) { Transaction tx = session.beginTransaction(); Person person1 = new Person(); Person person2 = new Person(); person1.setName("222"); person2.setName("111"); session.save(person1); session.save(person2); tx.commit(); } }
Person.java
public class Person { private int id; private String name; @Id @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } }
Этот код всегда делает ошибку в моем приложении:,
A different object with the same identifier value was already associated with the session
позже я узнал, что забыл автоматически увеличить свой первичный ключ!Мое решение - добавить этот код на ваш первичный ключ:
@GeneratedValue(strategy = GenerationType.AUTO)
источник
Это означает, что вы пытаетесь сохранить несколько строк в своей таблице со ссылкой на один и тот же объект.
проверьте свойство id вашего Entity Class.
@Id private Integer id;
к
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true, nullable = false) private Integer id;
источник
Перенести задачу присвоения идентификатора объекта из Hibernate в базу данных, используя:
<generator class="native"/>
Это решило проблему для меня.
источник
Добавьте аннотацию @GeneratedValue к вставляемому компоненту.
источник
Один из способов решить указанную выше проблему - переопределить расширение
hashcode()
.Также очистите сеанс гибернации до и после сохранения.
Явная установка отсоединенного объекта
null
также помогает.источник
Только что наткнулся на это сообщение, но в коде С #. Не уверен, что это актуально (хотя сообщение об ошибке точно такое же).
Я отлаживал код с помощью точек останова и расширял некоторые коллекции с помощью частных членов, пока отладчик находился в точке останова. Повторный запуск кода без рытья структур заставил сообщение об ошибке исчезнуть. Похоже, что просмотр частных коллекций с отложенной загрузкой заставил NHibernate загружать вещи, которые не должны были загружаться в то время (потому что они были в закрытых членах).
Сам код заключен в довольно сложную транзакцию, которая может обновлять большое количество записей и множество зависимостей как часть этой транзакции (процесса импорта).
Надеюсь, это ключ к разгадке всех, кто сталкивается с проблемой.
источник
Найдите в Hibernate атрибут «Cascade» и удалите его. Когда вы устанавливаете "Cascade" доступным, он будет вызывать другие операции (сохранение, обновление и удаление) для других сущностей, которые связаны со связанными классами. Так будет случиться такая же индивидуальная ценность. Со мной это сработало.
источник
У меня была эта ошибка несколько дней подряд, и я потратил слишком много времени на ее исправление.
public boolean save(OrderHeader header) { Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); try { session.save(header); for (OrderDetail detail : header.getDetails()) { session.save(detail); } transaction.commit(); session.close(); return true; } catch (HibernateException exception) { exception.printStackTrace(); transaction.rollback(); return false; } }
Прежде чем я получу эту ошибку, я не упомянул тип генерации идентификатора в объекте OrderDetil. когда без создания идентификатора OrderDetail он сохраняет значение 0 для каждого объекта OrderDetail. это то, что объяснил #jbx. Да, это лучший ответ. вот один пример, как это происходит.
источник
Попробуйте разместить код вашего запроса раньше. Это решит мою проблему. например, измените это:
к этому:
источник
возможно, вы не устанавливаете идентификатор объекта перед вызовом запроса на обновление.
источник
Я столкнулся с проблемой из-за неправильной генерации первичного ключа, когда я вставляю такую строку:
public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) { // TODO Auto-generated method stub try { Set<Byte> keySet = map.keySet(); for (Byte byte1 : keySet) { Device device=new Device(); device.setNumDevice(DeviceCount.map.get(byte1)); device.setTimestamp(System.currentTimeMillis()); device.setTypeDevice(byte1); this.getHibernateTemplate().save(device); } System.out.println("hah"); }catch (Exception e) { // TODO: handle exception logger.warn("wrong"); logger.warn(e.getStackTrace()+e.getMessage()); } }
Я меняю класс генератора идентификаторов на идентификатор
<id name="id" type="int"> <column name="id" /> <generator class="identity" /> </id>
источник
В моем случае только flush () не работал. Мне пришлось использовать clear () после flush ().
public Object merge(final Object detachedInstance) { this.getHibernateTemplate().flush(); this.getHibernateTemplate().clear(); try { this.getHibernateTemplate().evict(detachedInstance); } }
источник
если вы используете EntityRepository, используйте saveAndFlush вместо save
источник
Если оставить вкладку выражений в моей IDE открытой, что вызывает вызов hibernate для объекта, вызывающего это исключение. Я пытался удалить этот же объект. Также у меня была точка останова на вызове удаления, которая, похоже, необходима для возникновения этой ошибки. Простое создание другой вкладки выражений в качестве передней вкладки или изменение настройки, чтобы ide не останавливался на точках останова, решило эту проблему.
источник
Убедитесь, что ваша сущность имеет тот же тип генерации, что и все сопоставленные сущности.
Пример: UserRole
public class UserRole extends AbstractDomain { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String longName; private String shortName; @Enumerated(EnumType.STRING) private CommonStatus status; private String roleCode; private Long level; @Column(columnDefinition = "integer default 0") private Integer subRoleCount; private String modification; @ManyToOne(fetch = FetchType.LAZY) private TypeOfUsers licenseType;
}
Модуль:
public class Modules implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String longName; private String shortName;
}
Главный объект с отображением
public class RoleModules implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) private UserRole role; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) private Modules modules; @Type(type = "yes_no") private boolean isPrimaryModule; public boolean getIsPrimaryModule() { return isPrimaryModule; }
}
источник
В дополнение ко всем предыдущим ответам, возможное решение этой проблемы в крупномасштабном проекте, если вы используете объект значения для своих классов, не устанавливая атрибут id в классе VO Transformer.
источник
просто зафиксируйте текущую транзакцию.
теперь вы можете начать другую транзакцию и делать что угодно с сущностью
источник
Другой случай, когда такое же сообщение об ошибке может быть сгенерировано, настраиваемое
allocationSize
:@Id @Column(name = "idpar") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence") @SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20) private Long id;
без соответствия
alter sequence par_idpar_seq increment 20;
может вызвать проверку ограничения во время вставки (что легко понять) или «другой объект с таким же значением идентификатора уже был связан с сеансом» - этот случай был менее очевиден.
источник