ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-11-11 15:19:52  阅读:144  来源: 互联网

标签:spring-boot hibernate spring java


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

例如:

我(通过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
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>

HibernateUtils.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应该看起来像这样.

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

这样做时,您可能会遇到一个错误,指出由于没有事务(或类似的东西)而找不到会话.

要解决这个问题,请使用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将为您创建一个数据源.接下来,删除您的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的注入,您将获得一个托管会话,该会话将为您正确打开和关闭.

控制器修复

您的控制器也存在缺陷,因为您应该注入一个PasswordDao而不是具体的类型(由于创建事务代理,该类型现在将失败).

@Controller
public class PasswordsController extends BaseControllerHelper {

    @Autowired
    private PasswordDAO passwordDAO;

JPA解决方案

但是,尽管所有这些可能都会起作用,但我强烈建议您使用JPA和EntityManager而不是Session和SessionFactory方法.

为此,请删除LocalSessionFactoryBean和HibernateTransactionManager并将hibernate.cfg.xml的其余属性添加到application.properties.

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> {}

开箱即用的所有杂项功能(findAll,findOne,保存等)均可用.创建查询非常容易,并且无需编写样例代码.

标签:spring-boot,hibernate,spring,java
来源: https://codeday.me/bug/20191111/2020714.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有