gpt4 book ai didi

java - 如何在 Spring Boot 1.5.1 Hibernate 中防止隐式缓存

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:35:43 27 4
gpt4 key购买 nike

我试图理解为什么在创建新实体并在应用程序运行时持久化它之后,在检索这些实体的列表时,应该从数据库中检索新实体,但不是吗?

例如:

我创建了一个新实体(从 UI)并像这样成功地持久化它:

@Repository
public class BaseDAOHibernate {

Session session;

public BaseDAOHibernate() {

session = HibernateUtils.getSessionFactory().openSession();
}

public void save(Object object) {
Transaction tx = session.beginTransaction();
session.save(object);
tx.commit();
}
...

我已验证该实体已保存在数据库中。

接下来,当我刷新列出这些实体的 UI 时(我为此向数据库添加了一个新实体),新实体不包括在内,也没有从以下内容中检索到:

@Repository
@SuppressWarnings("unchecked")
public class PasswordDAOHibernate extends BaseDAOHibernate implements PasswordDAO {

@Override
public Collection<Password> getPasswords() {

Query query = session.createQuery("select ...");
return query.list();
}

这是界面:

public interface PasswordDAO {

Password getPassword(Integer id);

Collection<Password> getPasswords();

Collection<Password> getPasswords(PasswordSearchParameters params);

Collection<PasswordType> getPasswordTypes();
}
...

从 Controller 调用:

@Controller
public class PasswordsController extends BaseControllerHelper {

@Autowired
private PasswordDAOHibernate passwordDAO;

@RequestMapping("/passwords.htm")
public void passwords(Map model,
HttpServletRequest request) {

Collection<Password> passwords = passwordDAO.getPasswords();
model.put("passwords", passwords);
}

这是当前设置的配置: hibernate .cfg.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.connection.url">jdbc:mysql://host:3306/server</property>
<property name="hibernate.connection.username">username</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping class="com.crm.entity.User"></mapping>
<mapping class="com.crm.entity.Role"></mapping>
<mapping class="com.crm.entity.Property"></mapping>
<mapping class="com.crm.entity.Menu"></mapping>
<mapping class="com.crm.entity.Password"></mapping>
<mapping class="com.crm.entity.PasswordType"></mapping>
</session-factory>
</hibernate-configuration>

hibernate 工具.java

@Component
public class HibernateUtils {

private static final SessionFactory sessionFactory = buildSessionFactory();

private static SessionFactory buildSessionFactory() {
try {
return new Configuration().configure().buildSessionFactory();

}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {
return sessionFactory;
}

public static void shutdown() {
getSessionFactory().close();
}
}

当我重新启动服务器应用程序时,新实体会显示在列表中。

是否存在某种我需要清除的隐式缓存?请注意,我在此阶段没有实现任何显式缓存。

最佳答案

问题出在您的 dao 上,它存在非常危险的缺陷。 Session 不是线程安全的,不应共享。它应该为每个操作打开(或者至少应该使用当前 session )。

普通 Hibernate 解决方案

你的 DAO 应该看起来像这样。

private final SessionFactory sessionFactory;

protected BaseDAOHibernate(SessionFactory sessionFactory) {
this.sessionFactory=sessionFactory;
}

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

public void save(Object object) {
getCurrentSession().save(object);
}

现在您的特定 dao 应该重用 getSession 方法。

@Repository
@Transactional
public class PasswordDAOHibernate extends BaseDao implements PasswordDao {

@Autowired
public PasswordDAOHibernate(SessionFactory sessionFactory) {
super(sessionFactory);
}

@Override
public Collection<Password> getPasswords() {
return getSession.query("select ...", Password.class).list();
}

这样做时,您可能会遇到错误,指出由于没有事务无法找到 session (或类似的东西)。

要解决此问题,请使用 Spring Boot(以及一些手动配置)。

首先将 hibernate.connection 属性移动到您的 application.properties 并将它们从 hibernate.cfg.xml 中删除。

spring.datasource.url=jdbc:mysql://host:3306/server
spring.datasource.username=username
spring.datasource.password

现在 Spring 将为您创建一个 Datasource。接下来删除您的 HibernateUtils 并使用 Springs LocalSessionFactoryBean 配置 SessionFactory

@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setDataSource(dataSource);
return factory;
}

您还需要合适的事务管理器

@Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}

现在,由于在 Spring 中设置了所有内容,SessionFactory@Transactional 的注入(inject),您将获得一个托管的 Session,它将为您正确打开和关闭。

Controller 修复

您的 Controller 也有缺陷,因为您应该注入(inject) PasswordDao 而不是具体类型(由于为交易创建代理,现在会失败)。

@Controller
public class PasswordsController extends BaseControllerHelper {

@Autowired
private PasswordDAO passwordDAO;

JPA解决方案

然而,尽管所有这些都可能有效,但我强烈建议使用 JPA 和 EntityManager 而不是 SessionSessionFactory 方法。

为此,删除 LocalSessionFactoryBeanHibernateTransactionManager 并将 hibernate.cfg.xml 的其余属性添加到 应用程序属性

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true

spring.datasource 属性旁边就是您所需要的,您可以删除 hibernate.cfg.xml 文件。

现在不要使用 SessionFactory,而是在您的 dao 中使用 EntityManager

public abstract class BaseDao {

@PersistenceContext
protected EntityManager em;

public void save(Object o) {
em.persist(o);
}
}

还有你的具体道。

@Repository
@Transactional
public PasswordJpaDao extends BaseDao implements PasswordDao {

@Override
public Collection<Password> getPasswords() {
return em.createQuery("select ...", Password.class).getResultList();
}

Spring Data JPA解决方案

使用 JPA 时,您甚至可以放弃通用的 dao 方法和实现并使用 Spring Data JPA反而。你的整个 PasswordDao 看起来像

public interface PasswordDao extends JpaRepository<Password, Long> {}

所有 crud 功能(findAllfindOnesave 等)都是开箱即用的。创建查询非常简单,并且可以让您免于编写样板代码。

关于java - 如何在 Spring Boot 1.5.1 Hibernate 中防止隐式缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43015713/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com