Обновление объекта (отношение OneToMany)

у меня есть 2 объекта, студент и телефон, и связь между ними один ко многим.

@Entity 
@Table(name = "STUDENT") 
public class Student { 

    private long studentId; 
    private String studentName; 
    private Set<Phone> studentPhoneNumbers = new HashSet<Phone>(0); 

    public Student() { 
    } 

    public Student(String studentName, Set<Phone> studentPhoneNumbers) { 
        this.studentName = studentName; 
        this.studentPhoneNumbers = studentPhoneNumbers; 
    } 

    @Id 
    @GeneratedValue 
    @Column(name = "STUDENT_ID") 
    public long getStudentId() { 
        return this.studentId; 
    } 

    public void setStudentId(long studentId) { 
        this.studentId = studentId; 
    } 

    @Column(name = "STUDENT_NAME", nullable = false, length = 100) 
    public String getStudentName() { 
        return this.studentName; 
    } 

    public void setStudentName(String studentName) { 
        this.studentName = studentName; 
    } 

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

    public Set<Phone> getStudentPhoneNumbers() { 
        return this.studentPhoneNumbers; 
    } 

    public void setStudentPhoneNumbers(Set<Phone> studentPhoneNumbers) { 
        this.studentPhoneNumbers = studentPhoneNumbers; 
    } 

} 


@Entity 
@Table(name = "PHONE") 
public class Phone { 

    private long phoneId; 
    private String phoneType; 
    private String phoneNumber; 

    private Student student; 
    private Set<Student> students = new HashSet<Student>(0); 

    public Phone() { 
    } 

    public Phone(String phoneType, String phoneNumber) { 
        this.phoneType = phoneType; 
        this.phoneNumber = phoneNumber; 
    } 

    @Id 
    @GeneratedValue 
    @Column(name = "PHONE_ID") 
    public long getPhoneId() { 
        return this.phoneId; 
    } 

    public void setPhoneId(long phoneId) { 
        this.phoneId = phoneId; 
    } 

    @Column(name = "PHONE_TYPE", nullable = false, length=10) 
    public String getPhoneType() { 
        return this.phoneType; 
    } 

    public void setPhoneType(String phoneType) { 
        this.phoneType = phoneType; 
    } 

    @Column(name = "PHONE_NUMBER", nullable = false, length=15) 
    public String getPhoneNumber() { 
        return this.phoneNumber; 
    } 

    public void setPhoneNumber(String phoneNumber) { 
        this.phoneNumber = phoneNumber; 
    } 

    @ManyToOne(cascade = CascadeType.ALL) 
    @JoinColumn (name="STUDENT_ID") 
    public Student getStudent() { 
        return this.student; 
    } 

    public void setStudent(Student student) { 
        this.student = student; 
    }   

вот код applicationContext.xml:

  ... 
 <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">   
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>   
        <property name="url" value="jdbc:mysql://localhost/Project"/>   
        <property name="username" value="root"/>   
        <property name="password" value="root"/>   
    </bean>   

    <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">   
        <property name="dataSource" ref="myDataSource" />   
        <property name="annotatedClasses">   
            <list>   
                <value>com.domain.Student</value>                         
                <value>com.domain.Phone</value>                           
            </list>   
        </property>   
        <property name="hibernateProperties">   
            <props>   
                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>   
                <prop key="hibernate.show_sql">true</prop>   
                <prop key="hibernate.hbm2ddl.auto">create</prop>   
            </props>   
        </property>   
    </bean>   

 <bean id="myClassDAO" class="com.project.dao.ClassDAOImpl"> 
        <property name="sessionFactory" ref="mySessionFactory"/> 
 </bean>   

...  

И мой ClassDAOImpl выглядит так:

    public class ClassDAOImpl{ 

    private HibernateTemplate hibernateTemplate; 
    private Session session; 
    public void setSessionFactory(SessionFactory sessionFactory) { 
            this.hibernateTemplate = new HibernateTemplate(sessionFactory); 
            this.session = sessionFactory.openSession(); 
    }     

    public void updateStudent(){ 

            Transaction transaction = session.beginTransaction();           
            Student s = (Student)session.get(Student.class, new Long(1));   
            Set<Phone> phoneNumbers =s.getStudentPhoneNumbers();   
            phoneNumbers.add(new Phone ("house","12342342"));   
            s.setStudentPhoneNumbers(phoneNumbers);   
            session.update(s);   
            transaction.commit(); 

    } 

}  

Я просто хочу добавить телефон студенту с первичным ключом "1". Для этого я сначала получаю студента, а затем обновляю телефонный аппарат, добавляя новый. Наконец, я обновляю объект Student. Я посмотрел на свою таблицу телефонов, и там была новая строка с новым телефоном, но внешний ключ (STUDENT_ID) появился со значением «Null», поэтому связь не зарегистрирована. Как я могу это исправить?

заранее спасибо


person cracq    schedule 08.07.2011    source источник


Ответы (3)


Попробуй это:

public void updateStudent(){ 

        Transaction transaction = session.beginTransaction();           
        Student s = (Student)session.get(Student.class, new Long(1));   
        Set<Phone> phoneNumbers =s.getStudentPhoneNumbers();   
        Phone phone = new Phone("house", "12342342");
        phone.setStudent(s);
        phoneNumbers.add(phone);   
        s.setStudentPhoneNumbers(phoneNumbers);   
        session.update(s);   
        transaction.commit(); 

    }

Или еще лучше, добавьте служебный метод для ученика следующим образом:

public void addPhone(Phone phone) {
    if (phone == null) throw new IllegalArgumentException("Cannot add a null phone to a Student!");
    phone.setStudent(this);
    getStudents().add(phone);
}

И измените свой метод updateStudent следующим образом:

public void updateStudent(){ 

        Transaction transaction = session.beginTransaction();           
        Student s = (Student)session.get(Student.class, new Long(1));   
        Phone phone = new Phone("house", "12342342");
        s.addPhone(phone);
        session.update(s);   
        transaction.commit(); 

    } 

Также убедитесь, что вы установили параметры каскадирования на каскадирование на телефон.

person Nobody    schedule 08.07.2011
comment
Как упомянул Дж. Б. Низе, вам также следует прочитать об управлении транзакциями в Spring. Но поскольку этот вопрос касался обновления сущностей, я не уверен, что это актуально. - person Nobody; 09.07.2011

В двунаправленных отношениях сущностей всегда рекомендуется связывать обе стороны отношения. И это несмотря на то, что у вас есть владеющая сторона отношения (сторона, не отмеченная «mappedBy»).

Так, например, когда вы создаете новую связь, вызовите getStudentPhoneNumbers().addPhone(newPhone) для сущности студента, а также setStudent() для сущности телефона.

person Basanth Roy    schedule 08.07.2011

В JPA/Hibernate вы всегда должны самостоятельно поддерживать обе стороны двунаправленной ассоциации. Сторона-владелец ассоциации, которая является стороной, используемой Hibernate, чтобы узнать, существует ли ассоциация или была ли она изменена, — это сторона, у которой есть атрибут нет mappedBy.

В вашем случае это Phone, который является владельцем. Таким образом, если вы не установите свойство student созданного вами телефона, Hibernate будет считать, что телефон не связан ни с одним пользователем, даже если у пользователя есть телефон в списке телефонов.

Примечание: прочтите документацию Spring о декларативном управлении транзакциями. Вы действительно не делаете того, что нужно делать, чтобы получить открытую сессию и управлять транзакциями.

person JB Nizet    schedule 08.07.2011
comment
что вы имеете в виду в своем примечании? Я заметил, что если я закрываю сеанс после фиксации транзакции, я могу выполнить метод updateStudentone только один раз, во второй раз, когда я это делаю, спящий режим говорит, что соединение закрыто. Я новичок в этом, и я не совсем понимаю документацию Spring, не могли бы вы объяснить мне, что я делаю неправильно? - person cracq; 09.07.2011
comment
Объявите PlatformTransactionManager в файле конфигурации Spring, используйте @Transactional в своих транзакционных методах и получите сеанс, используя sessionFactory.getCurrentSession(), чтобы получить сеанс. Вы должны запускать и останавливать транзакции, открывать и закрывать сеансы программно. Весна должна сделать все это за вас. Прочтите параграф 11.5.6 документации и используйте ту же конфигурацию, за исключением bean-компонента txManager, который должен быть org.springframework.orm.hibernate3.HibernateTransactionManager и быть объявлен, как показано в разделе 14.3.3. - person JB Nizet; 09.07.2011
comment
Я пытался, но это не сработало. Не могли бы вы добавить несколько строк кода в мой код, чтобы он работал? Это студенческий проект, и у меня не так много времени, чтобы создать приложение... Буду очень признателен за вашу помощь. - person cracq; 11.07.2011
comment
В любом случае, что произойдет, если я ничего не изменю в коде? Это так серьезно? Как я уже сказал, у меня мало времени... Я должен закончить заявку на следующей неделе. - person cracq; 11.07.2011