Hibernate 和 Spring - 加载操作执行此操作,更新/删除操作则不然

发布于 2024-10-21 10:30:35 字数 7009 浏览 6 评论 0原文

我一直在关注这个教程: http://www.scribd.com/doc/25244173/Java -Struts-Spring-Hibernate-教程 设置(前面描述的)在教程文件中运行良好,但是当我进行更改时 - 删除/更新操作不会发生。没有错误或怪癖,它只是完全忽略我!至于检索数据 - 一切都很完美。

几乎所有来自 tut 的文件都是相同的,但有这些差异;本教程使用服务文件:

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

,我尝试使用父控制器和对 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();
    }

}

和 spring 的 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.springframework.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>

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.springframework.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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南渊 2024-10-28 10:30:35

您的代码看起来好像您没有完全理解 Spring 依赖注入的概念,因为您的 BaseController 负责 Spring 管理单例和提供依赖项的工作。

问题

您正在 BaseController 中自己创建 EventControllerPersonController 实例,而不是依赖 Spring。 Spring 无法拦截类实例的手动创建,也无法通过使用 Spring 注释 @Transactional 注释类来为它们提供此处所需的事务行为。

解决方案

那么,让我们找到一条清理这段代码的路径。首先删除类HibernateUtil,因为它是静态调用、java bean 概念和未使用的事务行为的混合体,同时它带来了一个新的抽象层,但没有任何好处。请记住也将其从 applicationContext.xml 中删除。

现在,从您的 applicationContext.xml 中删除 BaseController 并对其进行重大重写,因为它作为您的 PersonControllerPersonController 的单例工厂工作。 code>EventController 在这样的环境中,Spring 本身可以正确管理它。

public abstract class BaseController {

    private SessionFactory sessionFactory;

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

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

}

这样,BaseController 就成为其他扩展类的抽象基类,可以利用提供的 Hibernate Session 对象。现在让我们为您的 EventController 创建一个漂亮的界面。

public interface EventController {

    Event getEventById(long id);

    void deleteEventById(long id);

    Event createEvent(String name);

    List getEvents();

}

接下来,我们需要 Hibernate 的上述接口的实现,因此让我们利用之前创建的 BaseController 实现来调用新类 EventControllerHibernate

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

}

请记住在 Spring applicationContext.xml 中正确注册此类,以便也提供所需的 SessionFactory

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

如果您检索 EventController< 类型的 Spring bean /code> 从 Spring 中,您将获得一个事务感知代理对象,该对象完全实现您的 EventController 接口,委托给 EventControllerHibernate 内实现的业务逻辑。

记住new EventControllerHibernate() 永远不应该出现在您的应用程序中,因为这不会起到作用,因为 Spring 无法拦截类实例的手动创建!以编程方式获取事务感知实例如下所示:

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

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);
浅黛梨妆こ 2024-10-28 10:30:35

控制器代码不是很优雅。你应该遵循 codescape 在这里所说的一切。然而,问题的简单修复如下:您可以返回已注入 BaseController() 的相同 bean 实例,而不是返回 new PersonController() 或 new EventController()。这将返回一个代理对象,spring可以在该代理对象上拦截@Transactional。然而,正如我所说, 定义的控制器不是好的代码。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文