【问题标题】:Is Hibernate safer for Web Applications?Hibernate 对 Web 应用程序更安全吗?
【发布时间】:2017-02-03 07:34:21
【问题描述】:

我怀疑Hibernate 对于 Web 应用程序(JSP、Servlet、Hibernate、MySQL)是否安全,因为它会“同时”获得“不同用户”的数千次访问。

我担心的原因如下。

假设我有一个针对学校学生的 Web 应用程序。他们有自己的个人资料,并将维护自己的学生课程、分数等。现在,肯定会有超过 1 个用户始终在线,拥有自己的个人资料。这意味着如果用户 A 对他的数学分数进行了编辑,它将在用户 As 个人资料中完成。它永远不会在同时在线的所有用户AB 和'C 的个人资料中被替换。

Servlets 是多线程的以提供上述支持。它与纯 JDBC 的预期效果一样好。 Hibernate 怎么样?

我还上传了我的HibernateUtil 供您参考。就我而言,我将其称为SessionFactoryBuilder

public class SessionFactoryBuilder
{
    private static SessionFactoryBuilder instance;
    private static SessionFactory sessionFactory;

    private SessionFactoryBuilder()
    {
        buildConfig();
        System.out.println("hehehehe");
    }

    private static void buildConfig()
    {
        Configuration configuration = new Configuration().configure();
            StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
            sessionFactory = configuration.buildSessionFactory(builder.build());
    }

    public static SessionFactoryBuilder getInstance()
    {
         if(instance == null)
         {
            instance = new SessionFactoryBuilder();

         }
      return instance;
    }

    public SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }

}

【问题讨论】:

  • 是的,hibernate 可以安全使用。它只是为您提供 ORM 关系......其余的一切都与 JDBC 中的完全相同
  • @Naruto:即使在我给定的示例中,多个用户将同时在线并且没有数据重叠?
  • @Drew: errr....我很困惑??
  • 这是个玩笑
  • @Drew:好的,所以安全吗?

标签: java mysql multithreading hibernate servlets


【解决方案1】:

1) 在 Hibernate 中,每个应用程序都存在一个 Single SessionFactory 对象
SessionFactory 的内部状态是不可变的所以它是线程安全的。多个线程可以同时访问它以获取 Session 实例。

以下代码描述了通过 Utility 类获取 SessionFactory 实例的标准方法。

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

/**
 * Hibernate Utility class with a method to get Session Factory object.
 */
public class HibernateUtil {
private static final SessionFactory sessionFactory;//Once created, its properties cannot be changed

static {
    try {
        // Create the SessionFactory from standard (hibernate.cfg.xml) config file.

        sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

    } catch (Throwable ex) {

        // Log the exception.
        System.err.println("Initial SessionFactory creation failed." + ex);
        throw new ExceptionInInitializerError(ex);
    }
}

public static SessionFactory getSessionFactory() {

    return sessionFactory;

}
}

2) Hibernate Session是java应用层和hibernate之间的接口。这是用于执行数据库操作的核心接口。 Session 的生命周期以逻辑事务的开始和结束为界。

Hibernate Session 对象不是线程安全的,每个线程都应该获取自己的会话实例并在工作完成后关闭它。

这并不意味着/意味着实现者是线程安全的。相反,每个线程/事务都应该从 SessionFactory 获取自己的实例。

A typical transaction should use the following idiom:

 Session sess = factory.openSession();
 Transaction tx;
 try {
     tx = sess.beginTransaction();
     //do some work
     ...
     tx.commit();
     }
 catch (Exception e) {
    if (tx!=null) tx.rollback();
    throw e;
 }
 finally {
      sess.close();
 }

如果 Session 抛出异常,则必须回滚事务并丢弃会话。 异常发生后,Session的内部状态可能与数据库不一致。

2.1) 下面列出了两种广泛用于获取 Hibernate Session 对象的方法。

  1. openSession //用于多线程环境
  2. getCurrentSession //用于单线程环境

Hibernate SessionFactory getCurrentSession() 方法返回绑定到上下文的会话。 但是要使其工作,我们需要在 hibernate 配置文件中对其进行配置。由于这个会话对象属于休眠上下文,我们不需要关闭它。一旦 SessionFactory 关闭,这个会话对象就会关闭。

<property name="hibernate.current_session_context_class">thread</property>

Hibernate SessionFactory openSession() 方法总是打开一个新会话。完成所有数据库操作后,我们应该关闭这个会话对象。
我们应该在多线程环境中为每个请求打开一个新会话。 p>


2.2) 还有另一种方法可以使用 openStatelessSession() 创建 Hibernate Session 对象,它为您提供休眠无状态会话。

它是一个面向命令的 API,用于对数据库执行批量操作。
无状态会话不实现一级缓存,也不与任何二级缓存交互,也不实现事务性后写或自动脏检查,也不将操作级联到关联的实例。无状态会话会忽略集合。通过无状态会话执行的操作绕过 Hibernate 的事件模型和拦截器。由于缺少一级缓存,无状态会话容易受到数据别名效应的影响。

对于某些类型的事务,无状态会话的执行速度可能比有状态会话稍快。(例如:批处理/批量更新)

StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")
.scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
session.update(customer);
}

tx.commit();
session.close();

在此代码示例中,查询返回的 Customer 实例会立即分离。它们从不与任何持久性上下文相关联。

StatelessSession 接口定义的 insert()、update() 和 delete() 操作被认为是直接的数据库行级操作。它们分别导致立即执行 SQL INSERT、UPDATE 或 DELETE。

无状态会话不是线程安全的,使用无状态会话时可能发生异常是“org.hibernate.AssertionFailure:可能对会话进行非线程安全访问”


3) 您的学生记录项目是一个多线程应用程序,因此您在使用休眠时需要小心。尝试通过打开新会话、使用事务、提交和回滚以及在需要时关闭会话来使用最佳编程实践。

我个人在我们的项目中使用过 hibernate,我们有数百万用户通过 hibernate 作为后端 API 访问数据库。我们在多线程环境中从未遇到过此类问题,因为我们使用了休眠的最佳编程实践。即使数据库发生任何异常,整个事务也会回滚。

因此,与 JDBC 相比,可以以更高的成功率实现数据库事务的 ACID 属性(原子性、一致性、隔离性、持久性)。

【讨论】:

  • 好的,谢谢您的详细解答。所以简而言之,Hibernate 对于成千上万用户同时访问的 Web 应用程序是安全的。不是吗? (是的,打开/关闭会话和事务回滚需要发生才能实现这一点。)。在您的回答中,您提到 transactions must beroll back if any error ,现在这也适用于select statements 吗?在 JDBC 中我们不这样做,因为在 select 中没有什么可以回滚的?
  • 是的,它对 Web 应用程序来说是安全的。在休眠状态下,SELECT 不需要回滚,因为 select 查询永远不会改变数据库的状态。
  • 谢谢。那我应该用什么? getCurrentSession()openSession() ?我正在使用openSession()
  • 您应该使用 openSession() 因为您的应用程序处于多线程环境中。 getCurrentSession 应该用于单线程应用程序,这里只有一个会话可用,并且它的生命周期由休眠上下文管理,因此无需关闭它。
  • multi-threaded environment - 你的意思是 servlets 不是吗?据我所知,它们默认是多线程的
猜你喜欢
  • 2011-07-29
  • 2013-08-12
  • 2011-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多