Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java 8 lambda myMap.stream().count() != myMap.size() after merging myMap

EDIT: I found this beeing a bug of eclipse link: https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075

ORIGINAL QUESTION:

In a JUnit test I found unexpected behaviour. As you can see the last assertion fails, because the stream seems to contain no data, while the map is not empty?! I found out, that it has something to do with 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;
    }

}

AM:

@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 dependencies

    <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>
like image 303
Stuck Avatar asked Jul 05 '26 14:07

Stuck


1 Answers

As Boann did, I tried running your code (filling in simple implementations for classes you haven't shown the code for (LV, AbstractModule) and initializing lv) and I get the expected result:

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

So, my assumption is that the problem isn't in the code you have shown here, but in the code for either LV or AbstractModule, or perhaps you aren't importing the classes you think you are. Finally, can I assume there is no other thread running that could be changing the ams collection?

For comparison, this is my dummy implementation of your classes.

AbstractModule:

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

initialization:

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

EDIT:

After seeing your updated question, and realizing that all these classes are in fact persistent database Entities, I think that the problem may be due to lazy loading. Could I suggest that you change your definition of ams to

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

As you can see from the discussion below, it turns out that EclipseLink implements the ams collection as an org.eclipse.persistence.indirection.IndirectSet, which is a lazily loaded implementation. But that class does not implement the stream() method.

As the OP has noted, there is a bug report against EclipseLink to get this fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075

like image 124
Breandán Dalton Avatar answered Jul 07 '26 02:07

Breandán Dalton



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!