Hi!
As a convenience, I let most entities inherit from a common
@MappedSuperclass with some fields that are used in pretty much all of
the entities.
Here is a simplified part of it:
@MappedSuperclass
public class CommonPersistence {
@Column(name = "created_on")
private Date createdOn;
@PrePersist
protected void onCreate() {
createdOn = new Date();
}
public Date getCreatedOn() {
return createdOn;
}
...
}
However,
the createdOn field does not seem to be included in the generated query
type. I looked at path initialization in the docs, but I can't figure
out how to include the superclass fields.
Is it possible to include fields from a superclass in a query?
timowestIs CommonPersistence in the same compilation unit / module like your
subclasses? If not, then that's probably the reason for your problem.
In our tests the superclass fields / properties are available in the subtype querytypes.
Can you give me the fully qualified class names of your annotations? Just to be sure.
jmbeYou are correct. The superclass is in another maven module.
The annotations are all from javax.persistence, but from
<dependency>
<groupId>org.hibernate.java-persistence</groupId>
<artifactId>jpa-api</artifactId>
<version>2.0-cr-1</version>
</dependency>
I tried creating a new superclass and subclass for testing in the same module, and then a
_super field showed up properly in the query type.
Is
there any way I can access the fields from the superclass, even though
it is in another module? Can I add the path myself in a query somehow?
timowestI will test this locally a bit and provide a solution.
Fields from classes in external locations are now made available since release 1.3.4.
jmbeNice! Works fine when the entity inherits directly from a simple
@MappedSuperclass, but there seems to be a problem with the way I use
generics in the superclass.
The actual problem I'm having leads
to a compilation error for unresolved fields, but I think the root cause
might be the same as in the simplified example below.
@MappedSuperclass
public class CommonPersistence {
@Column(name = "created_on")
private Date createdOn;
}
@MappedSuperclass
public class CommonIdentifiablePersistence<ID extends Serializable> extends
CommonPersistence {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(updatable = false)
private ID id;
}
@Entity
public class Entity extends CommonIdentifiablePersistence<Long> {
}
In the example QEntity.id will have the type
//inherited
public final PSimple<java.io.Serializable> id = _super.id;
but shouldn't it be PNumber?
//inherited
public final PNumber<Long> id;
Is it possible to deduce the type of id is a Long?
timowestYes, this is a known issue. I will provide a fix this week.
jmbeI've captured the compilation error I alluded to earlier in a test, if you would like to try it.
package com.mysema.query.domain;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import java.util.Date;
import org.junit.Ignore;
import org.junit.Test;
import com.mysema.query.annotations.QueryEntity;
import com.mysema.query.annotations.QuerySupertype;
import com.mysema.query.types.path.PDateTime;
import com.mysema.query.types.path.PNumber;
/**
* Test multiple level superclasses with generics.
*/
public class Inheritance6Test {
/*
* Top superclass.
*/
@QuerySupertype
public static class CommonIdentifiable<ID extends Serializable> {
@SuppressWarnings("unused")
private ID id;
@SuppressWarnings("unused")
private Date createdOn;
}
/*
* Intermediate superclass, equivalent to @MappedSuperclass.
*/
@QuerySupertype
public abstract static class Translation<T extends Translation<T, K>, K extends TranslationKey<T, K>>
extends CommonIdentifiable<Long> {
@SuppressWarnings("unused")
private String value;
}
/*
* Intermediate superclass, equivalent to @MappedSuperclass.
*/
@QuerySupertype
public abstract static class TranslationKey<T extends Translation<T, K>, K extends TranslationKey<T, K>>
extends CommonIdentifiable<Long> {
}
@QueryEntity
public static class Gloss extends Translation<Gloss, GlossKey> {
}
@QueryEntity
public static class GlossKey extends TranslationKey<Gloss, GlossKey> {
}
@Test
public void gloss_subtype_should_contain_fields_from_superclass() {
assertEquals(String.class, QInheritance6Test_Gloss.gloss.value
.getType());
}
@Test
public void intermediate_superclass_should_contain_fields_from_top_superclass() {
/*
* This won't compile when Translation is marked as @QuerySupertype -
* works fine as @QueryEntity.
*/
// assertEquals(PDateTime.class,
// QInheritance6Test_Translation.translation.createdOn.getClass());
}
@Test
public void gloss_subtype_should_contain_fields_from_top_superclass() {
assertEquals(PDateTime.class, QInheritance6Test_Gloss.gloss.createdOn
.getClass());
}
@Test
@Ignore
public void gloss_subtype_should_contain_id_from_top_superclass() {
assertEquals(PNumber.class, QInheritance6Test_Gloss.gloss.id.getType());
}
}
When
Translation is marked as @QueryEntity, then the fields from the
superclass gets correctly generated and the tests passes (except for the
id type as you already know).
However, when Translation is
marked as @QuerySupertype, then the fields from the top superclass
doesn't seem to be generated in the query type. Translation is supposed
to be an abstract @MappedSuperclass to I think @QuerySupertype is the
equivalent annotation in the tests, right?
Never mind! The field is correctly included in the subtype, where it should be available.
Still, the other project won't compile properly - will do some more experimenting tomorrow.
timowestThe Translation issue is not a bug. Your test case should be
@Test
public void intermediate_superclass_should_contain_fields_from_top_superclass() {
QInheritance6Test_Translation translation = QInheritance6Test_Gloss.gloss._super;
assertEquals(PDateTime.class, translation.createdOn.getClass());
}
For
@MappedSuperclass and @QuerySupertype annotated types, the default
variable is not created, since these types are not entity types, just
supertypes with common mappings.
The constructors for Q-types of mapped supertypes are also different.
And I am working on the generics issues. A fix will be available soon.
jmbeYeah, I tripped myself up when switching back and forth between
@QueryEntity and @QuerySupertype when testing. Sorry about that.
timowestNo problem

The generics issues have been fixed in release 1.3.11. The subtype
properties have now types that reflect the generic parameter bindings of
the subtype.
Sorry that it took some time.
jmbeYes, I can confirm that the generic type is generated correctly now!
I'll
have another go at reproducing the compilation problem I've been
experiencing since after 1.3.3 (last version that works). It definitely
seems to be caused by inheriting from a supertype in another module.
Sorry for posting this much code.

But for the java classes at least, you can just paste the text into
the correct package and Eclipse will create proper source files.
Add to QuerydslAnnotationProcessorTest (this test passes):
@Test
public void processGenericInheritanceWithSuperclassInExternalModule() throws IOException {
process(Collections.singletonList(packagePath + "Inheritance8Test.java"));
}
New test in querydsl-apt (second test fails):
package com.mysema.query.domain;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import org.junit.Test;
import com.mysema.query.annotations.QueryEntity;
import com.mysema.query.annotations.QuerySupertype;
import com.mysema.query.test.domain.CommonPersistence;
import com.mysema.query.types.path.PNumber;
public class Inheritance8Test {
@QuerySupertype
public static class CommonIdentifiable<ID extends Serializable> extends CommonPersistence {
@SuppressWarnings("unused")
private ID id;
}
@QueryEntity
public static class SimpleSubclass extends CommonPersistence {
}
@QueryEntity
public static class GenericSubclass extends CommonIdentifiable<Long> {
}
@Test
public void simple_subclass_should_contain_fields_from_external_superclass() {
assertEquals(PNumber.class, QInheritance8Test_SimpleSubclass.simpleSubclass.version.getClass());
}
@Test
public void generic_subclass_should_contain_fields_from_external_superclass() {
assertEquals(PNumber.class, QInheritance8Test_GenericSubclass.genericSubclass.version.getClass());
}
}
New test-jar module
querydsl-test
with this pom (note, I haven't bothered to add versions to
<dependencyManagement> in the root pom, so there is some
duplication here):
<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-root</artifactId>
<version>1.3.11-SNAPSHOT</version>
</parent>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-test</artifactId>
<name>Querydsl - Test support</name>
<packaging>jar</packaging>
<properties>
<jdo.version>2.3-eb</jdo.version>
</properties>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
<!-- license : Apache License 2.0 -->
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- test -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo2-api</artifactId>
<version>${jdo.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.4.0.GA</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>maven-apt-plugin</artifactId>
<version>0.3.1</version>
<configuration>
<outputDirectory>target/generated-test-sources/java</outputDirectory>
</configuration>
<executions>
<execution>
<id>jpa</id>
<phase>generate-test-sources</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
<execution>
<id>jdo</id>
<phase>generate-test-sources</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
<processor>com.mysema.query.apt.jdo.JDOAnnotationProcessor</processor>
</configuration>
</execution>
<execution>
<id>qd</id>
<phase>generate-test-sources</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
<processor>com.mysema.query.apt.QuerydslAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>target/generated-test-sources/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
One test class in
querydsl-test/src/test/java:
package com.mysema.query.test.domain;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
@MappedSuperclass
public class CommonPersistence {
@SuppressWarnings("unused")
@Version
private Long version;
}
And finally, the new dependency in
querydsl-apt/pom.xml
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-test</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
<type>test-jar</type>
</dependency>
The
only test that fails is the last one in Inheritance8Test, but the
generated Q-query type contains a compilation error for an unresolved
field. Also, it looks like the class from the external module,
QCommonPersistence, is generated both in its containing module and then
again in querydsl-apt. That might be by design though?
Oops, disregard the last sentence about QCommonPersistence showing up
twice! I just hadn't cleaned up properly from an earlier test.
timowestThe issue with external supertypes has been fixed now. The release with the fixes is 1.3.12.
You mixed QuerySupertype and MappedSuperclass in your test case, but otherwise it described your problem quite well.
Thanks again for the bug report.
jmbeThe module where I first encountered the compilation problem works flawlessly now. Thanks!
Still,
there is one module that won't compile. On the surface, it is exactly
the same setup as the other one (it is just another module in the same
project).
I have this
@Entity
public class Account extends CommonIdentifiablePersistence<Long> {
}
but
I get an NPE on type.getCategory() on line 98 in TypeMappings when
getting the query type for CommonIdentifiablePersistence (which is in
another module)
java.lang.NullPointerException
at com.mysema.query.codegen.TypeMappings.getQueryType(TypeMappings.java:98)
at com.mysema.query.codegen.TypeMappings.getPathType(TypeMappings.java:94)
at com.mysema.query.codegen.TypeMappings.getPathType(TypeMappings.java:90)
at com.mysema.query.codegen.EntitySerializer.introSuper(EntitySerializer.java:342)
at com.mysema.query.codegen.EntitySerializer.intro(EntitySerializer.java:210)
at com.mysema.query.codegen.EntitySerializer.serialize(EntitySerializer.java:409)
at com.mysema.query.apt.Processor.serialize(Processor.java:355)
at com.mysema.query.apt.Processor.process(Processor.java:151)
at com.mysema.query.apt.jpa.JPAAnnotationProcessor.process(JPAAnnotationProcessor.java:45)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.j
ava:624)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnviron
ment.java:553)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.ja
va:698)
at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:981)
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:727)
at com.sun.tools.javac.main.Main.compile(Main.java:353)
at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:115)
at com.mysema.maven.apt.AbstractProcessorMojo.execute(AbstractProcessorMojo.java:148)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
QAccount
gets properly generated (and it gets generated before the supertype -
does the order matter?), but QCommonIdentifiablePersistence gets cut in
half:
package se.bliss.persistence;
import com.mysema.query.types.path.*;
import static com.mysema.query.types.path.PathMetadataFactory.*;
/**
* QCommonIdentifiablePersistence is a Querydsl query type for CommonIdentifiablePersistence
*/
public class QCommonIdentifiablePersistence extends PEntity<CommonIdentifiablePersistence<? extends java.io.Serializable>> {
private static final long serialVersionUID = 1999697412;
public static final QCommonIdentifiablePersistence commonIdentifiablePersistence = new QCommonIdentifiablePersistence("commonIdentifiablePersistence");
I have verified using mvn dependency:tree that I am using version 1.3.12 of querydsl.
I
have so far been unable to produce a failing test for this, so the
problem might just as well be in my own setup. I think I'll be able to
set aside some time for this either during the weekend or next week, so
that I can provide you with a proper test case.
By the way, where is the query type for a superclass supposed to be
generated when referring to a superclass from another module?
Everything
seems fine if I just remove the incorrect file because it will then use
(the correctly generated) QCommonIdentifiablePersistence from the
module where CommonIdentifiablePersistence lives.
Would there be
any problems with simply skipping generating query types for external
classes? I guess one problem is knowing whether a query type actually
exists in the external module or not, so that is why you are always
generating one.
timowestThe NPE issues have been fixed in 1.3.13
> By
the way, where is the query type for a superclass supposed to be
generated when referring to a superclass from another module?
The newest release generates the Q-types only for cases, that are not yet available in the classpath.
Thanks very much for the test cases. They are a good help for making Querydsl more robust.
jmbeConfirmed fixed!
I can now use Querydsl in all modules in all of my main projects.
Thank you very much, and have a nice weekend!
timowestGreat!
Have a nice weekend too.