-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: JPA joinList and treat
PostPosted: Tue Sep 03, 2013 3:25 am 
Newbie

Joined: Tue Sep 03, 2013 2:18 am
Posts: 1
I'm doing some comparison between EclipseLink and Hibernate 4.3 with JPA 2.1.

I found a difference between both implementations with joinList and treat:

Code:
    private static void runDemo( EntityManager em ) throws Exception
    {
        em.getTransaction().begin();
       
        TestEntity e;
       
        long start = System.currentTimeMillis();
       
        //let us create 10 dummy data, every one will have a TestEntityMultiExtensionA instance
        //5 of them will have a TestEntityMultiExtensionB instance
        for( int i=0; i<10; i++ )
        {
            e = new TestEntity();

            e.customValues = new ArrayList<TestKeyValueEntity>();
           
            TestEntityExtensionA meA = new TestEntityExtensionA();
            meA.setParent( e );
            meA.customValueA = "extA_"+i;
            e.setExtension( meA );
           
            if( i % 2 == 0 )
            {
                TestEntityExtensionB meB = new TestEntityExtensionB();
                meB.customValueB = "extB_"+i;
                meB.setParent( e );
                e.setExtension( meB );
            }

            em.persist( e );
        }
        em.getTransaction().commit();
       
        System.out.println( "\n\nCreation Time: " + ( System.currentTimeMillis() - start ) + "ms" );
       
        //for testing without use of cache
        em.clear();
        em.getEntityManagerFactory().getCache().evictAll();
       
        start = System.currentTimeMillis();
       
        List<Tuple> result;
       
        final CriteriaBuilder cb = em.getCriteriaBuilder();
       

        //creating a Tuple query from TestEntity
        final CriteriaQuery<Tuple> cq = cb.createTupleQuery();
        final Root<TestEntity> root = cq.from( TestEntity.class );

        final List<Selection<?>> selections = new LinkedList<Selection<?>>();

        selections.add( root.get( "id" ) );
       
        //now let us join to TestEntityMultiExtensionA and TestEntityMultiExtensionB:
       
        final ListJoin< TestEntity, TestEntityExtension > baseJoinExtA = root.<TestEntity, TestEntityExtension>joinList( "extensions", JoinType.LEFT );
        //additional on restriction to fetch only instances of TestEntityMultiExtensionA
        baseJoinExtA.on( cb.equal( baseJoinExtA.get( "extensionType" ), TestEntityExtensionA.class.getName() ) );
        //treat joined TestEntityExtension as TestEntityExtensionA to provide access of TestEntityExtensionA properties
        final ListJoin<TestEntity, TestEntityExtensionA> joinExtA = cb.treat(
                baseJoinExtA,
                TestEntityExtensionA.class
        );
        selections.add( joinExtA.get( "id" ) );
        selections.add( joinExtA.get( "customValueA" ) );
       
       
        final ListJoin< TestEntity, TestEntityExtension > baseJoinExtB = root.<TestEntity, TestEntityExtension>joinList( "extensions", JoinType.LEFT );
        //additional on restriction to fetch only instances of TestEntityMultiExtensionB
        baseJoinExtB.on( cb.equal( baseJoinExtB.get( "extensionType" ), TestEntityExtensionB.class.getName() ) );
        //treat joined TestEntityExtension as TestEntityExtensionB to provide access of TestEntityExtensionB properties
        final ListJoin<TestEntity, TestEntityExtensionB> joinExtB = cb.treat(
                baseJoinExtB,
                TestEntityExtensionB.class
        );
        selections.add( joinExtB.get( "id" ) );
        selections.add( joinExtB.get( "customValueB" ) );

        cq.multiselect( selections );
       
        result = em.createQuery( cq ).getResultList();
       
        System.out.println( "\n\nFetch Time: " + ( System.currentTimeMillis() - start ) + "ms" );
       
        for( Tuple r : result )
            System.out.println( Arrays.toString( r.toArray() ) );
       
        em.close();
    }


Entities:
Code:
@MappedSuperclass
//EclipseLink Annotation
@PrimaryKey(validation=IdValidation.ZERO)
public abstract class Entity {
   
    //JPA Annotations
    @Id
    @Column(name="id")
    @GeneratedValue( generator = EntityConstants.HILO_GENERATOR )
    //Hibernate Annotations, IdGenerator is a custom extension of MultipleHiLoPerTableGenerator which uses the SimpleClass name as valueName
    @GenericGenerator( name = EntityConstants.HILO_GENERATOR, strategy = IdGenerator.NAME )   
    private long id;
   
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
}

@javax.persistence.Entity(name="tests")
public class TestEntity extends Entity{

    @OneToMany( cascade={CascadeType.ALL}, mappedBy="parent" )
    @BatchFetch( size=EntityConstants.DEFAULT_BATCH_FETCH_SIZE, value=BatchFetchType.IN )
    @CascadeOnDelete
    private List<TestEntityExtension> extensions;
       
    @Override
    public List<TestEntityExtension> getExtensions() {
        return extensions;
    }

    @Override
    public void setExtensions(List<TestEntityExtension> extensions) {
        this.extensions = extensions;
    }

    @Override
    public <EE extends TestEntityExtension> EE getExtension( Class<EE> extension) {
        if( extensions == null || extension == null )
            return null;
       
        for( E ext : extensions )
        {
            if( ext.getClass() == extension )
                return (EE) ext;
        }
       
        return null;
    }

    @Override
    public void setExtension( TestEntityExtension extension ) {
        if( extensions == null )
            extensions = new ArrayList<TestEntityExtension>();
       
        if( extension != null )
        {
            @SuppressWarnings("unchecked")
            final TestEntityExtension existingExtension = getExtension( extensions, (Class<? extends TestEntityExtension>)extension.getClass() );
           
            if( existingExtension == null )
                extensions.add( extension );
            else if( existingExtension != extension )
            {
                extensions.remove( existingExtension );
                extensions.add( extension );
            }
        }
    }
}

@javax.persistence.Entity(name="tests_extensions")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn( name=IExtension.EXTENSION_TYPE_COLUMN_NAME, discriminatorType=DiscriminatorType.STRING )
@DiscriminatorOptions( force=true, insert=true)
public abstract class TestEntityExtension extends Entity
{
    @Column(name=IExtension.EXTENSION_TYPE_COLUMN_NAME)
    private String extensionType = getClass().getName();
   
    public String getExtensionType() {
        return extensionType;
    }
   
    @ManyToOne
    @JoinColumn(name="test_id", nullable=false)
    private TestEntity parent;

    @Override
    public TestEntity getParent() {
        return parent;
    }

    @Override
    public void setParent(TestEntity parent) {
        this.parent = parent;
    }
}

@Entity(name="tests_extensions_a")
public class TestEntityExtensionA extends TestEntityExtension {
    @Column(name = "custom_value_a")
    public String customValueA;
}

@Entity(name="tests_extensions_b")
public class TestEntityExtensionB extends TestEntityExtension {
    @Column(name = "custom_value_b")
    public String customValueB;
}




With EclipseLink the whole thing works like expected:
Code:
Creation Time: 22ms


Fetch Time: 9ms
[1, 1, extA_0, 2, extB_0]
[2, 3, extA_1, 0, null]
[3, 4, extA_2, 5, extB_2]
[4, 6, extA_3, 0, null]
[5, 7, extA_4, 8, extB_4]
[6, 9, extA_5, 0, null]
[7, 10, extA_6, 11, extB_6]
[8, 12, extA_7, 0, null]
[9, 13, extA_8, 14, extB_8]
[10, 15, extA_9, 0, null]


But with Hibernate i get following error:
Code:
Creation Time: 22ms
java.lang.IllegalArgumentException: Unable to resolve attribute [customValueA] against path [null]
   at org.hibernate.jpa.criteria.path.AbstractPathImpl.unknownAttribute(AbstractPathImpl.java:112)
   at org.hibernate.jpa.criteria.path.AbstractPathImpl.locateAttribute(AbstractPathImpl.java:209)
   at org.hibernate.jpa.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:180)
   at JpaTest.runDemo(Tester2.java:119)
   at JpaTest.main(Tester2.java:41)


So i checked the sources of Hibernate and patched the Class org.hibernate.jpa.criteria.path.ListAttributeJoin:

Code:
    //treat patch member
    private ManagedType<?> treatedManagedType = null;
   
    @Override
    public <T extends E> ListAttributeJoin<O,T> treatAs(Class<T> treatAsType) {
        //original implementation:
        //return new TreatedListAttributeJoin<O,T>( this, treatAsType );
       
        this.resetJavaType( treatAsType );
        this.treatedManagedType = criteriaBuilder().getEntityManagerFactory().getMetamodel().managedType( treatAsType );
        return (ListAttributeJoin<O, T>) this;
    }
   
    @Override
    protected ManagedType<E> locateManagedType() {
        if( treatedManagedType == null )
            return super.locateManagedType();
        else
            return (ManagedType<E>) treatedManagedType;
    }


Now the whole thing works like expected with Hibernate:
Code:
Creation Time: 20ms


Fetch Time: 20ms
[1, 1, extA_0, 2, extB_0]
[2, 3, extA_1, null, null]
[3, 4, extA_2, 5, extB_2]
[4, 6, extA_3, null, null]
[5, 7, extA_4, 8, extB_4]
[6, 9, extA_5, null, null]
[7, 10, extA_6, 11, extB_6]
[8, 12, extA_7, null, null]
[9, 13, extA_8, 14, extB_8]
[10, 15, extA_9, null, null]


For me the original Implementation is a bug.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.