2013-03-07 3 views
2

У меня есть две таблицы: родительский и дочерний имеет @oneTomany отношения между ними. следующая моя структура таблицы.Hibernate не обновляет дочерний объект при обновлении родительского объекта

Структура таблицы

CREATE TABLE `parent` (
    `id_parent` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id_parent`) 
) 

CREATE TABLE `child` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    `parent_id` int(3) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `fk_parent_child` (`group_id`), 
    CONSTRAINT `fk_parent_child` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id_parent`) 
) 

Я создал классы сущностей для этого, как следует

родительского класса.

@Entity 
@Table(name = "parent") 
public class Parent { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id_parent") 
    private int id; 

    @Column(name = "name") 
    private String name; 

    @OneToMany(mappedBy = "defaultchild", fetch = FetchType.EAGER) 
    @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE }) 
    private Set<Child> childs; 

    //setter and getters. 
} 

Детский класс.

@Entity 
@Table (name = "child") 
public class Report { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column (name = "id") 
    private int id; 
    @Column (name = "name") 
    private String name; 

    @ManyToOne 
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,org.hibernate.annotations.CascadeType.MERGE}) 
    @JoinColumn(name="parent_id") 
    private Parent defaultchild;  

    @ManyToMany(fetch = FetchType.LAZY) 
    @JoinTable(name = "group",joinColumns={@JoinColumn(name="fk_child_id")},inverseJoinColumns={@JoinColumn(name="fk_group_id")}) 
    private Set<XXX> groups = new HashSet<XXX>(0); 
    //Setter and Getter methods 
} 

Класс обслуживания

public UIGroup getParentByName(String name) { 
    return DAO.getParentByName(name); 
} 

@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
public boolean updateParent(Parent parent) { 
    return DAO.updateParent(parent); 
} 


@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
public boolean deleteParent(Parent parent) { 
    return DAO.deleteParent(parent); 
} 

DAO класса.

public UIGroup getParentByName(String name) { 
    Query query; 
    Parent parent = null; 
    try { 
     String queryString = " from Parent where name = :name"; 
     query = sessionFactory.getCurrentSession().createQuery(queryString); 
     query.setParameter("name", name); 
     uiGroup = (Parent) query.uniqueResult(); 
    } catch (Exception e) { 
     logger.error(e); 
    } 
    return parent; 
} 

public boolean updateParent(Parent parent) { 
    boolean result = true; 
    Session session = null; 
    Transaction tx = null; 
    try { 
     session = sessionFactory.getCurrentSession(); 
     tx = session.beginTransaction(); 
     session.merge(parent); 
     tx.commit(); 
     session.flush(); 
    } catch (HibernateException e) { 
     result = false; 
     logger.error(e); 
    }// end of try-catch block. 
    return result; 
} 


public boolean deleteParent(Parent parent) { 
    boolean result = true; 
    Session session = null; 
    try { 
     session = sessionFactory.getCurrentSession(); 
     session.delete(parent); 
    } catch (HibernateException e) { 
     result = false; 
     logger.error(+ e); 
    } 
    return result; 
} 

, но когда я пытаюсь вызвать следующий код

Parent otherParent = Service.getParentByName("Other"); 
Parent parent = Service.getParentByName("XYZ"); 
//here I am assigning childs assign to XYX parent to other Parent 
Set<Child> childs = new TreeSet<Child>(Child.COMPARE_BY_ID); 
childs.addAll(otherParent.getchildes()); 
childs.addAll(parent.getchilde()); 
otherParent.setChilds(childs); 
Service.updateParent(otherParent); 
Service.deleteParent(parent); 

Я получаю следующее сообщение об ошибке.

Ошибка

java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`database`.`child`, CONSTRAINT `fk_parent_child` FOREIGN KEY (`parent_id`) REFERENCES `child` (`id_parent`)) 

, который означает, что мой код обновления не работает должным образом Ниже логарифм Service.updateParent (otherParent) заявление

SELECT parent0_.id_parent AS id1_135_1_, parent0_.name AS name135_1_, childs1_.parent_id AS parent4_3_, childs1_.id AS id3_, childs1_.id AS id143_0_, childs1_.child_name AS child2_143_0_, childs1_.is_sea_child AS is3_143_0_, childs1_.parent_id AS parent4_143_0_ 
FROM ui_parent parent0_ 
LEFT OUTER JOIN child childs1_ ON parent0_.id_ui_parent=childs1_.parent_id 
WHERE parent0_.id_parent=1 

пожалуйста, помогите мне, что я не знать, что пошло не так с этим кодом заранее.

+0

Является ли это JPA или спящий режим? Я спрашиваю, потому что у вас есть уровень обслуживания JPAish с аннотациями @Transactional, но тогда ваши DAO, похоже, управляют транзакциями самостоятельно. Это намеренно? – wallenborn

ответ

2

Я бы, наверное, просто написать метод DAO moveAllChildren (String srcName, String dstName), как это:

public class ParentDAO { 
    @PersistenceContext 
    private EntityManager em; 

    public Parent findParentByName(name) { 
     TypedQuery<Parent> q = em.createQuery("select p from Parent where p.name = :name", Parent.class); 
     return q.setParameter("name", name).getSingleResult(); 
    } 

    public void moveAllChildren(String srcName, String dstName) { 
     Parent src = findParentByName(srcName); 
     Parent dst = findParentByName(dstName); 
     Set<Child> children = new HashSet<Child>(); 
     for (Child c: src.getChildren()) { 
     children.add(c); 
     } 
     src.getChildren().removeAll(children); 
     dst.getChildren().addAll(children); 
    } 
} 

В общем, при использовании каскадного, это хорошая практика, чтобы добавлять и удалять детей в явном виде, вместо того чтобы сказать dst.setChildren (allChildren), чтобы дать JPA возможность управлять обеими сторонами отношения. Если вы этого не сделаете, вы рискуете, что ребенок все еще считает, что src является их родителем, а затем вы, скорее всего, увидите нарушение ограничений из своей базы данных.

Кроме того, это хорошая идея попытаться позволить JPA управлять как можно большему количеству ваших сущностей. Поэтому я бы предпочел, чтобы приложение не вызывало вызов службы DAO для извлечения родителей только для перемещения своих детей, а затем вызывает тот же вызов службы того же DAO, чтобы объединить эти изменения в базу данных. Вам лучше реализовать это как операцию низкого уровня на уровне DAO, а затем добавить службу, которая вызывает это для обработки @Transaction.

+0

@welleborn ранее я использую транзакцию только в слое DAO, но это не делает никаких изменений в моей базе данных. После прочтения одной статьи я обнаружил, что мне нужно использовать ** @ Transactional **, поэтому я использовал ** @ Transactional ** Вот. –

+0

Я попробовал код, приведенный выше, и переместил всех детей из src в dst и обновил оба родителя, теперь я получаю ** org.hibernate.ObjectDeletedException: удаленный объект будет повторно сохранен cascade (удалить удаленный объект из ассоциаций) ** ошибка и когда я удаляю ** @ Cascade ({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.MERGE}) ** from ** @ ManyToOne ** Я получаю такое же сообщение об ошибке выше *** ограничение внешнего ключа не работает *** –

+0

У вас есть выбор , Вы можете позволить своим методам DAO начинать и совершать транзакции, или вы можете пометить сервисные методы как @Transactional и обработать данные диспетчера контейнеров. Последний метод является предпочтительным, поскольку он позволяет использовать методы DAO в качестве строительных блоков, не беспокоясь о том, что они мешают транзакциям друг друга. Но не используйте оба. Выбери один. – wallenborn

1

Если у вас нет конкретной причины, по которой вы решили ограничить свой тип @Cascade, вы можете переключиться на ВСЕ.

@Cascade({ CascadeType.ALL}) 

Или даже просто

@OneToMany(cascade=CascadeType.ALL, mappedBy="defaultchild") 

EDIT: вы неправильно «родитель» несколько раз, это не повредит, чтобы установить, что либо.

+0

спасибо за быстрый ответ. Я попробовал свой код, но дочерние записи удалялись, когда я использовал @OneToMany (cascade = CascadeType.ALL, mappedBy = "defaultchild") или @Cascade ({CascadeType.ALL}) –

+0

Фактически я просто даю сценарий моего проблема, изменяя имена сущностей, поэтому эти орфографические ошибки есть. –

+0

спасибо, сэр это помогло много – mikebertiean

0

Вы пытаетесь удалить parent, но у этого все еще есть childs. Попробуйте очистить childs коллекцию до удаления:

for(Child child : parent.getChilds()){ 
    child.setParent(null); 
} 
parent.getChilds().clear(); 
Service.deleteParent(parent); 

Вы не можете удалять parent если Child еще имеет ссылку на него.

+0

Спасибо за ваш ответ Я попробовал код, как указано выше, но он все еще дает мне ошибку выше ** Невозможно удалить или обновить родительскую строку: ограничение внешнего ключа не работает ** –

+0

В сообщении говорится: мне, что есть еще ссылка на 'parent'. Попытайтесь найти это в БД. И ваш код работает с 2 родителями: otherParent и Parent. Попытайтесь отделить два вызова, сделайте это как можно проще. Так было бы проще. –

+0

in for loop Я сделал некоторые изменения, например, я обновляю каждый дочерний объект, как child.setParent (otherParent); Service.updateChild (child), и я проверил журнал, и он дает запрос на обновление, но когда я проверяю базу данных, записи все еще не обновляются. –

0

, наконец, я получил решение с помощью Валленборн и после ссылки

JPA not saving foreign key to @OneToMany relation

здесь являются изменения, которые я сделал в моих классах.

Класс сущности.

@Entity 
@Table(name = "parent") 
public class Parent { 

    ..... 

    @OneToMany(cascade = CascadeType.PERSIST,mappedBy = "defaultchild", fetch = FetchType.EAGER) 
    private Set<Child> childs; 

    //setter and getters. 
} 

@Entity 
@Table (name = "child") 
public class Child { 

    .... 

    @ManyToOne 
    @JoinColumn(name="parent_id") 
    private Child defaultchild; 

    //Setter and Getter methods 
} 

Я изменил свое обслуживание и DAO классы, предложенные Валленборн следующим

класса обслуживания.

@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
public void moveAllChildren(String srcName, String dstName) { 
    DAO.moveAllChildren(srcName, dstName); 
} 

DAO Класс

public Parent findParentByName(name) { 
     TypedQuery<Parent> q = em.createQuery("select p from Parent where p.name = :name", Parent.class); 
     return q.setParameter("name", name).getSingleResult(); 
    } 

    public void moveAllChildren(String srcName, String dstName) { 
     Parent src = findParentByName(srcName); 
     Parent dst = findParentByName(dstName); 
     Set<Child> children = new HashSet<Child>(); 
     for (Child c: src.getChildren()) { 
     children.add(c); 
    } 
    src.getChildren().removeAll(children); 
    dst.getChildren().addAll(children); 
} 
Смежные вопросы