Java 如何强制提交 Spring - 安全休眠事务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24435989/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to force commit Spring - hibernate transaction safely
提问by Tim
We are using spring and hibernate for an web application: The application has a shopping cart where user can place items in it. in order to hold the items to be viewed between different login's the item values in the shopping cart are stored in tables. when submitting the shopping cart the items will be saved into different table were we need to generate the order number.
我们将 spring 和 hibernate 用于 Web 应用程序:该应用程序有一个购物车,用户可以在其中放置商品。为了在不同登录名之间保存要查看的项目,购物车中的项目值存储在表中。提交购物车时,如果我们需要生成订单号,项目将被保存到不同的表中。
When we insert the values into the table to get the order number, we use to get the max order number and add +1 to it. we are using spring transaction manager and hibernate, in the code flow we get the order number and update the hibernate object to hold the order num value. when i debug, i noticed that only when the complete transaction is issued the order number entity bean is being inserted.
当我们将值插入表中以获取订单号时,我们使用获取最大订单号并将其添加 +1。我们使用 spring 事务管理器和休眠,在代码流中我们获取订单号并更新休眠对象以保存订单号值。当我调试时,我注意到只有在发出完整的事务时,才会插入订单号实体 bean。
Issue here is when we two request is being submitted to the server at the same time, the same order number is being used, and only one request data is getting inserted. could not insert the other request value which is again a unique one. The order num in the table is a unique one.
这里的问题是当我们两个请求同时提交到服务器时,使用相同的订单号,并且只有一个请求数据被插入。无法插入另一个请求值,该值也是唯一的。表中的订单号是唯一的。
i noticed when debugging the persistant layer is not getting inserted into the database even after issuing session flush
session.flush()
我注意到在调试持久层时,即使在发出会话刷新后也没有插入到数据库中
session.flush()
its just updating the memory and inserting the data to db only at the end of the spring transaction . i tried explicitly issuing a commit to transaction
它只是在 spring 事务结束时更新内存并将数据插入数据库。我尝试明确发出交易承诺
session.getTransaction().commit();
session.getTransaction().commit();
this inserted the values into the database immediately, but on further code flow displayed message that could not start transaction.
这立即将值插入数据库,但在进一步的代码流中显示无法启动事务的消息。
Any help is highly appreciated.
任何帮助都受到高度赞赏。
Added: Oracle database i used. There is a sequence number which is unique for that table and also the order number maps to it.
补充:我使用的 Oracle 数据库。该表有一个唯一的序列号,并且订单号也映射到它。
采纳答案by Shailendra
Your first problem is that of serial access around the number generation when multiple thread are executing the same logic. If you could use Oracle sequences this would have been automatically taken care of at the database level as the sequences are guranteed to return unique values any number of times they are called. However since this needs to be now managed at server side, you would need to use synchronization mechanism around your number generation logic ( select max and increment by one) across the transaction boundary. You can make the Service method synchronized ( your service class would be singleton and Spring managed) and declare the transaction boundary around it. However please note that this would be have performance implications and is usually bad for scalability.
您的第一个问题是当多个线程执行相同的逻辑时围绕数字生成的串行访问。如果您可以使用 Oracle 序列,这将在数据库级别自动处理,因为序列保证在调用它们的任何次数时都返回唯一值。但是,由于现在需要在服务器端进行管理,因此您需要在跨事务边界的数字生成逻辑(选择最大值并增加一)周围使用同步机制。您可以使 Service 方法同步(您的服务类将是单例和 Spring 管理的)并声明围绕它的事务边界。但是请注意,这会对性能产生影响,并且通常不利于可扩展性。
Another option could be variation of this - store the id to be allocated in a seperate table with one column "currentVal" and use pessimistic lock for getting the next number. This way, the main table would not have any big lock. This way a lock would be held for the sequence generator code for the time the main entity creation transaction is complete. The main idea behind these techniques is to serialize access to the sequence generator and hold the lock till the main entity transaction commits. Also delay the number generator as late as possible.
另一种选择可能是这种变化 - 将要分配的 id 存储在一个单独的表中,其中包含一列“currentVal”,并使用悲观锁来获取下一个数字。这样,主表不会有任何大锁。这样,在主实体创建事务完成时,序列生成器代码将保持锁定。这些技术背后的主要思想是序列化对序列生成器的访问并保持锁定直到主实体事务提交。也尽可能延迟数字生成器。
The solution suggested by @Vlad is an good one if using triggers is fine in your design.
如果在您的设计中使用触发器很好,@Vlad 建议的解决方案是一个很好的解决方案。
Regarding your question around the flush behaviour, the SQL is sent to the database at flush call, however the data is not committed until the transaction is committed declaratively or a manual commit is called. The transaction can however see the data it purposes to change but not other transactions depending upon the isolation nature of transaction.
关于您关于刷新行为的问题,SQL 在刷新调用时发送到数据库,但是直到事务以声明方式提交或手动提交被调用时才会提交数据。然而,根据事务的隔离性质,事务可以看到它要更改的数据,但不能看到其他事务。
回答by Vlad Mihalcea
I would set the order number with a trigger which will run in the same transaction with the shopping cart insert one.
我将使用触发器设置订单号,该触发器将在与购物车插入的同一交易中运行。
After you save the shopping cart, to see the updated order count, you'll have to call:
保存购物车后,要查看更新的订单数量,您必须调用:
session.refresh(cart);
The count shouldn't be managed by Hibernate (insertable/updatable = false
or @Transient
).
计数不应由 Hibernate (insertable/updatable = false
或@Transient
) 管理。
回答by Opster Elasticsearch Pro-Vijay
follow these steps :- , 1) Create a service method with propagation REQUIRES_NEW in different service class . 2)Move your code (whatever code you want to flush in to db ) in this new method . 3)Call this method from existing api (Because of proxy in spring, we have to call this new service method from different class otherwise REQUIRES_NEW will not work which make sure your flushing data ).
请按照以下步骤操作:- , 1) 在不同的服务类中创建具有传播 REQUIRES_NEW 的服务方法。2)在这个新方法中移动你的代码(你想要刷新到 db 的任何代码)。3)从现有的api调用这个方法(由于spring中的代理,我们必须从不同的类调用这个新的服务方法,否则REQUIRES_NEW将不起作用,以确保您的刷新数据)。