Hibernate从缓存中返回错误的列表,即使预期列表与缓存列表不同

huangapple 未分类评论49阅读模式
英文:

Hibernate Returns Wrong List From Cache Even Expected List is Different Than the Cached One

问题

在下面的代码中,如果我不清除当前会话,则从该方法返回的仅是女孩的数量,即使我想返回该父母的所有子女的数量。

很明显,具有ID 1的父母有三个子女(2个女孩和1个男孩),但由于先前的检索方法仅返回带有女孩的父母,因此只返回女孩。当我清除会话以避免从缓存返回时,它按预期返回3。有人可以帮助我理解为什么会这样,并且如何在不清除当前会话的情况下避免这种情况?

以下是您提供的代码片段的翻译部分:

@Service
public class ExampleServiceImpl implements ExampleService {

    @Autowired
    private ExampleRepository exampleRepository;

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    @Transactional(readOnly = true)
    public int getNumberOfChildren() {
        List<Parent> parentList = exampleRepository.retrieveParentsWithGirls();

        //sessionFactory.getCurrentSession().clear();
        Parent parent = exampleRepository.retrieveParentWithId(1);

        System.out.println(parent.getChildSet().size());
        return parent.getChildSet().size();
    }
}

@Repository
public class ExampleRepositoryImpl implements ExampleRepository {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public List<Parent> retrieveParentsWithGirls() {
        CriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
        CriteriaQuery<Parent> criteria = builder.createQuery(Parent.class);
        Root<Parent> parentRoot = criteria.from(Parent.class);
        Fetch<Parent, Child> fetchChildren = parentRoot.fetch("childSet", JoinType.LEFT);
        Join<Parent, Child> joinChildren = (Join<Parent, Child>) fetchChildren;
        criteria.where(builder.equal(joinChildren.get("sex"), "girl"));
        criteria.distinct(true);

        return sessionFactory.getCurrentSession().createQuery(criteria).getResultList();
    }

    @Override
    public Parent retrieveParentWithId(int id) {
        CriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
        CriteriaQuery<Parent> criteria = builder.createQuery(Parent.class);
        Root<Parent> parentRoot = criteria.from(Parent.class);
        parentRoot.fetch("childSet", JoinType.LEFT);
        criteria.where(builder.equal(parentRoot.get("id"), id));

        return sessionFactory.getCurrentSession().createQuery(criteria).getSingleResult();
    }
}

@Entity
@Table(name = "child")
public class Child {
    // Child entity code...
}

@Entity
@Table(name = "parent")
public class Parent {
    // Parent entity code...
}

@EnableTransactionManagement
@Configuration
@Conditional(DatabaseRequiredCondition.class)
public class DatabaseConfiguration {
    // Database configuration code...
}

// SQL scripts
create table parent (id serial);
create table child (id serial, parent_id integer not null, sex character varying(10));

INSERT INTO public.parent values(default);
INSERT INTO public.parent values(default);

INSERT INTO public.child
(parent_id, sex)
VALUES(1, 'girl');

INSERT INTO public.child
(parent_id, sex)
VALUES(1, 'girl');

INSERT INTO public.child
(parent_id, sex)
VALUES(1, 'boy');

// Generated SQL scripts when running the code
Hibernate: 
    // SQL queries...

请注意,我已经省略了代码中的注释以及某些细节。如果您需要更详细的帮助,请随时提问。

英文:

In the code below if I don't clear current session, just the number of girls is returned from the method even if I want to return number of all children of this parent.

It's clearly seen that parent with id 1 has three children (2 girls and 1 boy), but just the girls are returned because of the previous retrieve method which returns parents with girls only. When I clear the session to avoid returning from cache, it returns 3 as expected. Can anybody help me understand why it is like this and how can I avoid this without clearing current session?

@Service
public class ExampleServiceImpl implements ExampleService {

    @Autowired
    private ExampleRepository exampleRepository;

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    @Transactional(readOnly = true)
    public int getNumberOfChildren() {
        List&lt;Parent&gt; parentList = exampleRepository.retrieveParentsWithGirls();

        //sessionFactory.getCurrentSession().clear();
        Parent parent = exampleRepository.retrieveParentWithId(1);

        System.out.println(parent.getChildSet().size());
        return parent.getChildSet().size();
    }
}

Let me share all the code I have as well as database scripts to make it more clear.

Repository:

@Repository
public class ExampleRepositoryImpl implements ExampleRepository {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public List&lt;Parent&gt; retrieveParentsWithGirls() {
        CriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
        CriteriaQuery&lt;Parent&gt; criteria = builder.createQuery(Parent.class);
        Root&lt;Parent&gt; parentRoot = criteria.from(Parent.class);
        Fetch&lt;Parent, Child&gt; fetchChildren = parentRoot.fetch(&quot;childSet&quot;, JoinType.LEFT);
        Join&lt;Parent, Child&gt; joinChildren = (Join&lt;Parent, Child&gt;) fetchChildren;
        criteria.where(builder.equal(joinChildren.get(&quot;sex&quot;), &quot;girl&quot;));
        criteria.distinct(true);

        return sessionFactory.getCurrentSession().createQuery(criteria).getResultList();
    }

    @Override
    public Parent retrieveParentWithId(int id) {
        CriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
        CriteriaQuery&lt;Parent&gt; criteria = builder.createQuery(Parent.class);
        Root&lt;Parent&gt; parentRoot = criteria.from(Parent.class);
        parentRoot.fetch(&quot;childSet&quot;, JoinType.LEFT);
        criteria.where(builder.equal(parentRoot.get(&quot;id&quot;), id));

        return sessionFactory.getCurrentSession().createQuery(criteria).getSingleResult();
    }
}

Entities:

@Entity
@Table(name = &quot;child&quot;)
public class Child {

    @Id
    @Column(name = &quot;id&quot;, unique = true, nullable = false)
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    @Column(name = &quot;sex&quot;)
    private String sex;

    @JoinColumn(name = &quot;parent_id&quot;, referencedColumnName = &quot;id&quot;)
    @ManyToOne(fetch = FetchType.LAZY)
    private Parent parent;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Parent getParent() {
        return parent;
    }

    public void setParent(Parent parent) {
        this.parent = parent;
    }
}

@Entity
@Table(name = &quot;parent&quot;)
public class Parent {

    @Id
    @Column(name = &quot;id&quot;, unique = true, nullable = false)
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;parent&quot;)
    private Set&lt;Child&gt; childSet;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Set&lt;Child&gt; getChildSet() {
        return childSet;
    }

    public void setChildSet(Set&lt;Child&gt; childSet) {
        this.childSet = childSet;
    }
}

DatabaseConfiguration:

@EnableTransactionManagement
@Configuration
@Conditional(DatabaseRequiredCondition.class)
public class DatabaseConfiguration {
    @Value(&quot;${jdbc.driverClassName}&quot;)
    private String DB_DRIVER;

    @Value(&quot;${jdbc.pwd}&quot;)
    private String DB_PASSWORD;

    @Value(&quot;${jdbc.url}&quot;)
    private String DB_URL;

    @Value(&quot;${jdbc.username}&quot;)
    private String DB_USERNAME;

    @Value(&quot;${hibernate.dialect}&quot;)
    private String HIBERNATE_DIALECT;

    @Value(&quot;${hibernate.showSql}&quot;)
    private String HIBERNATE_SHOW_SQL;

    @Value(&quot;${hibernate.packagesScan}&quot;)
    private String ENTITYMANAGER_PACKAGES_TO_SCAN;

    @Value(&quot;${hibernate.tx_timeout}&quot;)
    private Integer TIMEOUT_AS_SECONDS;

    @Bean
    @Primary
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(DB_DRIVER);
        dataSource.setUrl(DB_URL);
        dataSource.setUsername(DB_USERNAME);
        dataSource.setPassword(DB_PASSWORD);

        return dataSource;
    }

    @Bean
    @Primary
    public LocalSessionFactoryBean sessionFactory() throws IOException {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource());
        sessionFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);

        Properties hibernateProperties = new Properties();
        hibernateProperties.put(&quot;hibernate.dialect&quot;, HIBERNATE_DIALECT);
        hibernateProperties.put(&quot;hibernate.show_sql&quot;, HIBERNATE_SHOW_SQL);
        hibernateProperties.put(&quot;hibernate.format_sql&quot;, true);

        sessionFactoryBean.setHibernateProperties(hibernateProperties);

        return sessionFactoryBean;
    }


    @Bean(name=&quot;transactionManager&quot;)
    @Primary
    public HibernateTransactionManager transactionManager() throws IOException {
        HibernateTransactionManager transactionManager =
                new HibernateTransactionManager(sessionFactory().getObject());
        transactionManager.setDefaultTimeout(TIMEOUT_AS_SECONDS);
        return transactionManager;
    }
}

Scripts:

create table parent (id serial);
create table child (id serial, parent_id integer not null, sex character varying(10));
	
INSERT INTO public.parent values(default); 
INSERT INTO public.parent values(default); 
	
INSERT INTO public.child
(parent_id, sex)
VALUES(1, &#39;girl&#39;);

INSERT INTO public.child
(parent_id, sex)
VALUES(1, &#39;girl&#39;);

INSERT INTO public.child
(parent_id, sex)
VALUES(1, &#39;boy&#39;);

Generated Scripts When I run the code:

Hibernate: 
    select
        parent0_.id as id1_23_0_,
        childset1_.id as id1_5_1_,
        childset1_.parent_id as parent_i3_5_1_,
        childset1_.sex as sex2_5_1_,
        childset1_.parent_id as parent_i3_5_0__,
        childset1_.id as id1_5_0__ 
    from
        parent parent0_ 
    left outer join
        child childset1_ 
            on parent0_.id=childset1_.parent_id 
    where
        childset1_.sex=?
Hibernate: 
    select
        parent0_.id as id1_23_0_,
        childset1_.id as id1_5_1_,
        childset1_.parent_id as parent_i3_5_1_,
        childset1_.sex as sex2_5_1_,
        childset1_.parent_id as parent_i3_5_0__,
        childset1_.id as id1_5_0__ 
    from
        parent parent0_ 
    left outer join
        child childset1_ 
            on parent0_.id=childset1_.parent_id 
    where
        parent0_.id=1

Hibernate version: 5.4.5.Final

huangapple
  • 本文由 发表于 2020年4月11日 00:34:00
  • 转载请务必保留本文链接:https://java.coder-hub.com/61144588.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定