开发者

Hibernate and Spring - load operations do the trich, update/delete operations don't

开发者 https://www.devze.com 2023-02-16 15:27 出处:网络
I\'ve been following this tutorial: http://www.scribd.com/doc/25244173/Java-Struts-Spring-Hibernate-Tutorial

I've been following this tutorial: http://www.scribd.com/doc/25244173/Java-Struts-Spring-Hibernate-Tutorial The setup (described ahead) was working fine with the tutorial files, but when I have made the changes - delete / update actions just don't happen. No errors or quirks, it just completely ignores me! As for retrieving data - everything works perfectly..

Almost all the files from the tut are the same, with these differences; The tutorial uses a services file:

Services.java

package services;

import org.springframework.transaction.annotation.Transactional;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import data.*;
import java.util.List;

// This class is the business services tier in the application.
// @Transactional is needed so that a Hibernate transaction is set up,
//  otherwise updates won't have an affect
@Transactional
public class Services {
    // So Spring can inject the session factory
    SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory value) {
        sessionFactory = value;
    }

    // Shortcut for sessionFactory.getCurrentSession()
    public Session sess() {
        return sessionFactory.getCurrentSession();
    }

    public Event getEventById(long id) {
        return (Event) sess().load(Event.class, id);
    }

    public Person getPersonById(long id) {
        return (Person) sess().load(Person.class, id);
    }

    public void deleteEventById(long id) {
        sess().delete(getEventById(id));
    }

    public void deletePersonById(long id) {
        sess().delete(getPersonById(id));
    }

    public void createEvent(String name) {
        Event theEvent = new Event();
        theEvent.setName(name);
        sess().save(theEvent);
    }

    public void createPerson(String name) {
        Person p = new Person();
        p.setName(name);
        sess().save(p);
    }

    @SuppressWarnings("unchecked")
    public List getEvents() {
        return sess().createQuery("from Event").list();
    }

    @SuppressWarnings("unchecked")
    public List getPeople() {
        return sess().createQuery("from Person").list();
    }

    public void removePersonFromEvent(int personId, int eventId) {
        getEventById(eventId).getPeople().remove(getPersonById(personId));
    }

    public void addPersonToEvent(int personId, int eventId) {
        getEventById(eventId).getPeople().add(getPersonById(personId));
    }
}

and I have tried to separate the files by using a parent controller and static calls to HibernateUtil: HibernateUtil.java

package com.epa.util;

import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class HibernateUtil {

    // So Spring can inject the session factory
    static SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory value) {
        sessionFactory = value;
    }

    // Shortcut for sessionFactory.getCurrentSession()
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

BaseController.java

package com.epa.controller.base;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;

import com.epa.controller.EventController;
import com.epa.controller.PersonController;
import com.epa.util.HibernateUtil;

@Transactional
public class BaseController {

    protected SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    // Shortcut for sessionFactory.getCurrentSession()
    public Session sess() {
        return sessionFactory.getCurrentSession();
    }

    private PersonController personController = null;
    private EventController eventController = null;

    public PersonController getPersonController() {
        if (this.personController == null) {
            this.personController = new PersonController();
        }
        return personController;
    }

    public EventController getEventController() {
        if (this.eventController == null) {
            this.eventController = new EventController();
        }
        return eventController;
    }
}

EventController.java

package com.epa.controller;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import com.epa.controller.base.BaseController;
import com.epa.model.Event;

@Transactional
public class EventController extends BaseController {

    public Event getEventById(long id) {
        return (Event) sess().load(Event.class, id);
    }

    public void deleteEventById(long id) {
        sess().delete(getEventById(id));
    }

    public void createEvent(String name) {
        Event theEvent = new Event();
        theEvent.setName(name);
        sess().save(theEvent);
    }

    @SuppressWarnings("unchecked")
    public List getEvents() {
        return sess().createQuery("from Event").list();
    }

}

and spring's applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- The singleton hibernate session factory -->
    <bean id="sessionFactory" scope="singleton"
        class="org.springfr开发者_如何学编程amework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="configLocation" value="classpath:hibernate.cfg.xml" />
    </bean>

    <!-- Spring's hibernate transaction manager, in charge of making hibernate sessions/txns -->
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- So classes/functions with @Transactional get a hibernate txn -->
    <tx:annotation-driven />

    <!-- Inject my business services class to the actions 
    <bean id="services" class="com.epa.services.Services" scope="singleton">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>-->

    <!-- Inject my business services class to the actions -->
    <bean id="hibernateUtil" class="com.epa.util.HibernateUtil" scope="singleton">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean> 

    <bean id="baseController" class="com.epa.controller.base.BaseController" scope="singleton" />

</beans>


Your code looks as if you did not fully understand the concept of Spring dependency injection because your BaseController does Spring's job of managing singletons and suppling dependencies.

Problem

You are creating EventController and PersonController instances by yourself inside the BaseController instead of relying on Spring. Spring cannot intercept the manual creation of class instances and providing them with the transactional behavior which you require here by annotating the classes with the Spring annotation @Transactional.

Solution

So, lets find a path to clean this code up. First remove the class HibernateUtil because it is a mixture of static calls, java bean concepts and unused transactional behavior while it brings a new abstraction layer without any benefits. Remember to remove it from the applicationContext.xml also.

Now remove the BaseController from your applicationContext.xml also and give it a major rewrite because it works as a Singleton-Factory for your PersonController and EventController which would correctly managed by Spring itself in a environment like this.

public abstract class BaseController {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    protected Session getCurrentSession() {
        return sessionFactory.getCurrentSession();
    }

}

This way BaseController becomes an abstract base class for other extending classes, which can make use of the provided Hibernate Session object. Now lets create a nice interface for your EventController.

public interface EventController {

    Event getEventById(long id);

    void deleteEventById(long id);

    Event createEvent(String name);

    List getEvents();

}

Next we need the implementation of the above interface for Hibernate so let us call the new class EventControllerHibernate making use of the earlier created BaseController implementation.

public class EventControllerHibernate extends BaseController implements EventController {

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Event getEventById(long id) {
        return (Event) getCurrentSession().get(Event.class, id);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteEventById(long id) {
        getCurrentSession().delete(getEventById(id));
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Event createEvent(String name) {
         return (Event) getCurrentSession().save(new Event(name));
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    @SuppressWarnings("unchecked")
    public List getEvents() {
        return getCurrentSession().createQuery("from Event").list();
    }

}

And remember to register this class in your Spring applicationContext.xml correctly so that the required SessionFactory is provided, too:

<bean id="eventController" class="com.epa.controller.EventControllerHibernate">
    <property name="sessionFactory" ref="sessionFactory" />
</bean> 

If you retrieve a Spring bean of type EventController from Spring you will get a transaction-aware proxy object fully implementing your EventController interface delegating to the business logic implemented inside EventControllerHibernate.

Remember: A new EventControllerHibernate() should never occur in your application since this will not do the trick because Spring can not intercept the manual creation of a class instance! Getting a transaction-aware instance programmatically would look like this:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EventController eventController = context.getBean("eventController", EventController.class);


The controller codes are not very elegant. You should be following everything codescape has said here. However a simple fix to problem would be the following: Instead of returning a new PersonController() or new EventController(), you can return a bean instance of the same which have been injected into the BaseController(). This will return a proxy object on which the @Transactional can be intercepted by spring. However, as I said the controllers defined by are not good codes.

0

精彩评论

暂无评论...
验证码 换一张
取 消