【问题标题】:java 8 lambda myMap.stream().count() != myMap.size() after merging myMapjava 8 lambda myMap.stream().count() != myMap.size() 合并 myMap 后
【发布时间】:2014-11-24 14:52:04
【问题描述】:

编辑:我发现这是一个 Eclipse 错误链接:https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075

原始问题:

在 JUnit 测试中,我发现了意外行为。如您所见,最后一个断言失败了,因为流似乎不包含任何数据,而映射不为空?!我发现,它与 jpa/eclipselink 有关。

public class MyTest {
    private EntityManager em;

    @Before
    public void setUp() throws IOException {
            String dbFileName = "testDb.db";
            Map<String, Object> properties = new HashMap<String, Object>();
            properties.put(PersistenceUnitProperties.JDBC_URL, "jdbc:sqlite:"
                    + dbFileName);
            EntityManagerFactory factory = Persistence.createEntityManagerFactory("pul", properties);
            this.em = factory.createEntityManager();
    }

    @Test
    public void testStrange() {
        LV lv = new LV();
        // em.persist(lv); // this would work
        // em.merge(lv); // this would work as well
        lv = em.merge(lv); // this leads to the assertionError
        lv.getAms().add(new AM());
        assertEquals(1, lv.getAms().size()); // ok
        assertEquals(1, lv.getAms().stream().count()); // java.lang.AssertionError: expected:<1> but was:<0>
    }
}

LV:

@Entity
public class LV {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long _id;

    @OneToMany(mappedBy = "lv", cascade = CascadeType.ALL)
    private Collection<AM> ams = new HashSet<AM>();

    public LV() {
    }

    public Collection<AM> getAms() {
        return ams;
    }

}

上午:

@Entity
public class AM {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long _id;

    @ManyToOne
    private LV lv;

    public AM() {
    }

}

persistence.xml

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="pul" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.sqlite.JDBC" />
            <!-- url is set during runtime -->
            <!-- <property name="javax.persistence.jdbc.url" value="jdbc:sqlite:sample.db" 
                /> -->
            <property name="javax.persistence.jdbc.user" value="" />
            <property name="javax.persistence.jdbc.password" value="" />

            <!-- EclipseLink should create the database schema automatically -->
            <property name="eclipselink.ddl-generation" value="create-tables" />
            <property name="eclipselink.ddl-generation.output-mode"
                value="database" />
        </properties>
    </persistence-unit>
</persistence>

pom.xml 依赖项

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <dependency>
        <groupId>org.xerial</groupId>
        <artifactId>sqlite-jdbc</artifactId>
        <version>3.8.6</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>eclipselink</artifactId>
        <version>2.6.0-M3</version>
    </dependency>

【问题讨论】:

  • 了解lv.getAbstractModules() 和getKurzbezeichnung() 的类型可能会有所帮助。
  • 当我使用这些方法名称设置虚拟类时,您的确切代码对我有用(在列表中显示一致数量的项目)。所以我只能建议您的 lv.getAbstractModules() 方法不会随着时间的推移返回相同的列表。
  • 您是否有机会使用自定义的List 实现,该实现可能有一个损坏的stream() 方法?
  • 不,我使用 HashSet 将AbstractModules 存储在 LV 中。
  • 如果您使用的是 HashSet,您确定您已经正确地覆盖了 AbstractModule 的 equals 和 hashCode 方法,还是将它们都保留为默认实现?

标签: java dictionary lambda eclipselink java-stream


【解决方案1】:

正如 Boann 所做的那样,我尝试运行您的代码(为您未显示代码的类(LV,AbstractModule)填写简单的实现并初始化 lv),我得到了预期的结果:

2
before:short2
2
in map:short2, in map:short1
short2, short1
2

所以,我的假设是问题不在于您在此处显示的代码,而在于 LV 或 AbstractModule 的代码,或者您可能没有导入您认为的类。最后,我可以假设没有其他线程正在运行可以更改ams 集合吗?

为了比较,这是我对你的类的虚拟实现。

抽象模块:

public class AbstractModule {

    String kurzbezeichnung;

    public AbstractModule(String desc) {
        this.kurzbezeichnung = desc;
    }

    public String getKurzbezeichnung() {
        return this.kurzbezeichnung;
    }

}

LV:

public class LV {
    HashSet<AbstractModule> ams;

    public LV(HashSet<AbstractModule> abstractModules) {
        this.ams = abstractModules;
    }

    public Collection<AbstractModule> getAbstractModules() {
        return ams;
    }
}

初始化:

HashSet<AbstractModule> initialAms = new HashSet<AbstractModule>();
AbstractModule short1 = new AbstractModule("short1");
AbstractModule short2 = new AbstractModule("short2");
initialAms.add(short2);
initialAms.add(short1);
LV lv = new LV(initialAms);

编辑:

在看到您更新的问题并意识到所有这些类实际上都是持久性数据库实体后,我认为问题可能是由于延迟加载造成的。我能否建议您将 ams 的定义更改为

@OneToMany(mappedBy = "lv", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Collection<AM> ams = new HashSet<AM>();

从下面的讨论中可以看出,EclipseLink 将 ams 集合实现为 org.eclipse.persistence.indirection.IndirectSet,这是一个延迟加载的实现。但是该类没有实现stream() 方法。

正如 OP 所指出的,有一个针对 EclipseLink 的错误报告来解决这个问题:https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075

【讨论】:

  • 谢谢您的回复!没有其他线程在运行。原始类基本上是 POJO,并标记为由 JPA/eclipselink 管理的实体。我也写了假人,无法确认我的错误(基本上我实现了你展示的那些假人)。因此,我认为肯定有另一个部分会影响行为,但我无法找到它。但我发现,使用普通的 for 循环可以正常工作。这根本没有意义。所以我真的很困惑,所以问了,希望有人经历过类似的事情。
  • @Stuck 逐步调试代码,包括 java.util 类,找出发生了什么。如果这不能消除问题,请复制项目,然后删除部分直到问题消失,创建最小的代码来重现问题,然后在此处发布。
  • 对不起,我现在有时间给出一个完整的例子(参见第一篇文章中的更新)
  • 现在我看到这些是实体类,我更新了我的答案。
  • 谢谢,这是错误报告:bugs.eclipse.org/bugs/show_bug.cgi?id=433075 也许您可以更新您的答案,以便它包含它是间接集运行时类型的信息。谢谢你的提示!
猜你喜欢
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-27
相关资源
最近更新 更多