spring boot / jpa /maven / eclipse : metamodel classes not generating in project - java

I've been going thru a lot of docs and posts here and other places and have not found a solution to my problem.
When I build my spring boot / jpa / hibernate / maven project, the metamodel classes are generated in the target/generated-sources folder, but as per the jboss docs, when I set my java compiler -> annotation processing to generate metamodel classes in target/metamodel, no classes are generated, therefor my controller class is not able to access any of the Widget_ fields.
the relevant pom info is here:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>5.3.10.Final</version><!--$NO-MVN-MAN-VER$-->
<scope>provided</scope>
</dependency>
<build>
<finalName>SpringBootWebAppTest</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>10</source>
<target>10</target>
<compilerArguments>
<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
I have organized my classes along the lines of this stackoverflow q
#Entity
#Table(name = "widget")
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties(value = {"created", "lastEdited"}, allowGetters = true)
public class Widget {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Long id;
#NotNull(message = "name is required")
private String name;
#NotNull(message = "sku is required")
private long sku;
with a repository:
#Repository
public interface WidgetRepository extends JpaRepository<Widget, Long> {
}
a custom repository
public interface CustomWidgetRepository {
public List<Widget> getWidgetsMatchingName(String searchText);
}
and an impl:
#Service
public class CustomWidgetRepositoryImpl implements CustomWidgetRepository {
#PersistenceContext
EntityManager entityManager;
#Override
public List<Widget> getWidgetsMatchingName(String searchText) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Widget> query = criteriaBuilder.createQuery(Widget.class);
Root<Widget> widgetRoot = query.from(Widget.class);
// this doesn't compile, as metamodel classes are not created where they can be accessed
// query.where(criteriaBuilder.like(widgetRoot.get(Widget_.), "%" + searchText + "%"));
query.select(widgetRoot);
return entityManager.createQuery(query).getResultList();
}
}
according to this q (scroll down to march 2018 answer), just setting the compilier->annotation settings in eclipse should be enough. I came across a couple of resources, such as this one that describes how to add the target/generated-sources/annotations to the classpath, but not sure how old that post is, and wondering if it conflicts with the first link that says just to update the compiler->annotation settings.
I asked a related question with regards to metamodel classes, but that project did not use spring boot, and this current project is just a super simple test project to get spring boot jpa stuff running so I can migrate my main project over.
thanks for any help.
Update
It was this maven plugin that adds to target/generated-sources/annotations as a src folder that did the trick.

Related

JPAMetaModelEntityProcessor not working correctly with special characters

I am working on a spring-data-jpa project with oracle db. In my project, there is an entity called Booking -
#Entity
public class Booking {
public enum $State { ACCEPTED, REJECTED}
#Id
private Long id;
private $State state;
}
I am using hibernate as ORM. To generate metamodel for this entity I am using hibernate-jpamodelgen dependency -
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>5.4.22.Final</version>
</dependency>
When I run mvn clean install on my project, it starts building metamodel files inside target folder. But for this Booking class, it generates -
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Booking.class)
public abstract class Booking_ {
public static volatile SingularAttribute<Booking, .State> state;
}
During compilation it fails, as .State does not exist.
I don't understand why it is converting $ to ..
Does anyone know how to solve this issue?
The solution to this problem is we cannot use $ in class names.
$ is special character with respect to JAVA class names and internally used by JVM for processing inner classes. For more details check - https://hibernate.atlassian.net/browse/HHH-14277

Lazy fetching doesn't work on entity property in spring boot with #Basic annotation

I try to make JPA Entity's one of the members lazy-fetched in spring boot while using MySQL DB as data source (this might not be mandatory but I'd rather have it mentioned).
My entity looks like this:
#Entity
public class DiskFile {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private long id;
#NotEmpty
#Column(unique = true)
private String path;
#Lob
#Column (columnDefinition = "LONGBLOB")
#Basic (fetch = FetchType.LAZY)
private byte[] data;
public DiskFile() {
}
/* Getters and Setters */
}
As you'd guessed I want data member to be fetched with lazy pattern, because it usually contains megabytes of data. Situation gets even more dire when I try to fetch list of these entities and all of them need to fetch megabytes and megabytes of data.
I know that there are several questions like these on Stackoverflow and I tried solutions mentioned there.
I already know that for hibernate that annotation is only like a recommendation and he might forcefully try to fetch that member Eagerly. So I have added following line to my application.properties:
spring.jpa.open-in-view = false
And I have already added following plugin into my maven build configurations:
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
however I don't get any lazy-fetching with that solutions, since I can see megabytes of data flowing into my system after calling appropriate API endpoint for this entity.

How to generate Swagger codegen Java models as JPA Entities

I am using Swagger codegen to create Java models to be used in a Spring REST server, and would like to know how to get Swagger to declare each model as a JPA entity.
I generate the code with the swagger-codegen-maven-plugin as follows:
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>2.4.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/openApi/Rack.json</inputSpec>
<language>spring</language>
<groupId>com.me</groupId>
<artifactId>rest-server</artifactId>
<apiPackage>com.me.rest.api</apiPackage>
<modelPackage>com.me.rest.model</modelPackage>
<invokerPackage>com.me.rest.invoker</invokerPackage>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
<java8>true</java8>
<dateLibrary>java8</dateLibrary>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
As I have it now, this is the abbreviated java code that gets generated:
#Validated
#javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "...")
public class Rack {
#JsonProperty("id")
private Long id = null;
#JsonProperty("name")
private String name = null;
...
}
How do I get Swagger to add the #Entity and #Id JPA annotations, as follows?
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
#Validated
#javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "...")
public class Rack {
#Id
#JsonProperty("id")
private Long id = null;
#JsonProperty("name")
private String name = null;
...
}
This way, all I would have to do to get Spring to automatically expose these generated classes as REST APIs, would be to add the following to my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
Then I could create the JPA repositories with Spring-Data, as follows:
public interface RackRepository extends CrudRepository<Rack, Long> {
}
A PR has recently been merged fixing your issue : https://github.com/OpenAPITools/openapi-generator/pull/11775
You need to upgrade your Maven plugin to use the latest version (currently unreleased, only snapshot is available)
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>6.0.0-SNAPSHOT</version>
...
</plugin>
The configuration might be slightly different.
Then you need to add x-class-extra-annotation and x-field-extra-annotation in your spec.
For instance for the Pet Clinic:
schemas:
Pet:
type: object
x-class-extra-annotation: "#javax.persistence.Entity"
required:
- id
- name
properties:
id:
type: integer
format: int64
x-field-extra-annotation: "#javax.persistence.Id"
name:
type: string
tag:
type: string
While the right way to solve this surely is an extension of swagger-codegen (probably with the introduction of some kind of include/exclude config), I got away with a fairly simply post-processing of the generated files.
In contrast to the OP I use Gradle instead of Maven and leveraged its extended filtering functionality. For Maven it is probably necessary to run a Groovy-script by way of the Groovy-Maven-Plugin, since Maven only supports placeholder substitution (as does Ant, so using the AntRun-Plugin would also not work).
I used a simple heuristic to only include entities with an id - the logic is as follows:
for all Java-files containing an ID-field
include import statement for javax.persistence.* after the package declaration
add the #Entity-annotation before the class definition
for the ID-field, add the annotations #Id and #GeneratedValue
(based on field names, other annotations - #OneToMany etc. - may be added as well)
Gradle-users may find the following task useful as a start:
task generateJpaAnnotations(type: Copy) {
from "${swaggerSources.<modelName>.code.outputDir}/src/main/java"
into "<output dir>
include '**/*.java'
eachFile {
if (it.file.text.contains("private Long id")) {
filter { line -> line.contains('package') ? "$line\nimport javax.persistence.*;" : line }
filter { line -> line.contains('public class') ? "#Entity\n$line" : line }
filter { line -> line.contains('private Long id') ? "#Id\n#GeneratedValue(strategy=GenerationType.AUTO)\n$line" : line } }
}
}
So I'm actually asking myself the same question.
I found an example but the guy is simply re-defining his POJOs and providing a way to adapt the generated ones to the handwritten ones. Tedious and not evolutive.
Globally this could be hard because I'm not sure there is a way in your swagger to decide which POJO will be JPA enabled and maybe you don't want them all in your DB (?) Also, how to you tag the id in swagger?
If you know of such a way, you can always modify the mustache (pojo.mustache I guess) to give you the annotations you're missing.

SQL strings added more than once for: tablename

I'm bootstrapping a database using hibernate-maven-plugin, using models that it scans in the maven module it's executed in.
Unfortunately, it stops when hibernate throws this:
org.hibernate.tool.schema.spi.SchemaManagementException: SQL strings added more than once for: reference_data_source.UK-UK_9ec6wdvyj3mjagiptcnrq2txv
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.checkExportIdentifier(SchemaCreatorImpl.java:299)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.doCreation(SchemaCreatorImpl.java:255)
at org.hibernate.tool.schema.internal.SchemaCreatorImpl.doCreation(SchemaCreatorImpl.java:128)
at org.hibernate.tool.hbm2ddl.SchemaExport.<init>(SchemaExport.java:199)
So, I have two persistence units, and some tables exists in both. Hibernate seems to interpret this like the same table though, so when it tries to store the same index, but for another schema, it fails thinking it is a duplicate. Their code can be found here.
I'm not sure how to approach this, anyway to configure hibernate hbm2ddl to keep track of these different peristence units?
This is the configuration for the hibernate-maven-plugin:
<plugin>
<groupId>de.juplo</groupId>
<artifactId>hibernate-maven-plugin</artifactId>
<version>2.0.0</version>
<configuration>
<detail>true</detail>
<persistenceUnit>mainPersistenceUnit</persistenceUnit>
<driver>com.mysql.jdbc.Driver</driver>
<dialect>org.hibernate.dialect.MySQL5Dialect</dialect>
<force>true</force>
<url><![CDATA[jdbc:mysql://localhost/auto_bootstrap_schema]]></url>
<username>user</username>
<password>pass</password>
</configuration>
<executions>
<execution>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
</dependencies>
</plugin>
I had the same problem. The reason in my case was that I had three entities starting with same prefix name and a OneToMany relation to the each more specialized one:
Person
PersonCard
PersonCardLayout
Renaming my model to this solved my problem:
Person
Card
Layout
This seems to be a bug in Hibernate.
i have the same problem, looks like a bug in:
org.hibernate.mapping.UniqueKey#getExportIdentifier
#Override
public String getExportIdentifier() {
return StringHelper.qualify( getTable().getName(), "UK-" + getName() );
}
because the identifier was build only from table name but without schema/catalog of the table.
Therefore, if you have to entities with same table name but different schema and in these entities two properties with same name and "unique = true" you will drop into the bug.
Another solution to this is to make use of #Table.
For example:
#Table(name="PERSON")
This happened to me when I had two classes representing two entities (#Entity) as follows:
#Entity
public class User {...}
#Entity
public class UserRoles {...}
I fixed this error by specifying the table name as follows:
#Entity
#Table(name="USER")
public class User {...}
And:
#Table(name="USER_ROLES")
#Entity
public class UserRoles {...}
Not sure if this is the same as what I ran into, but I had the same error issue with base class marked with #Entity annotation: changing to #MappedSuperclass removed the error.
Good Luck,
Ray

Spring #QuerydslPredicate Questions

Libaries Used
Spring Boot 1.3.2.RELEASE
QueryDSL 3.7.2
QueryDSL Maven Plugin 1.1.3
Hibernate 4.3.11.Final
Issue
Currently, I have a Spring Boot application that has some basic CRUD functionality using Spring Data JPA (backed by Hibernate), and auditing using Spring Data Envers. I also have the following endpoint to retrieve a list of entities from:
http://localhost:8080/test-app/list
Now, I wanted to use the new QueryDSL support that Spring offers through the #QuerydslPredicate annotation. This works fine for most fields or sub-entities, but it doesn't appear to work for collections of sub-entities. The documentation, blog posts, etc. don't seem to cover this case - and the only information I could find is that it supports "in" for simple collections (i.e. collections of String, etc.).
So, my entity is set up something like so:
Person.java
#Data
#Entity
#Audited
public class Person {
#Id
private long id;
private String name;
private List<Pet> pets = new ArrayList<>();
}
Pet.java
#Data
#Entity
#Audited
public class Pet {
#Id
private long id;
private int age;
}
I generate my Q classes using the com.mysema.maven:apt-maven-plugin, which generates my QPerson with the following field:
public final ListPath<com.test.Pet, com.test.QPet> pets = this.<com.test.Pet, com.test.QPet>createList("pets", com.test.Pet.class, com.test.QPet.class, PathInits.DIRECT2);
If I try to query on this though, I get an exception:
Query:
http://localhost:8080/test-app/list?pets.age=5
Exception:
10:21:37,523 ERROR [org.springframework.boot.context.web.ErrorPageFilter] (http-/127.0.0.1:8080-1) Forwarding to error page from request [/list] due to exception [null]: java.lang.NullPointerException
at org.springframework.util.ReflectionUtils.getField(ReflectionUtils.java:143) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:185) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:188) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPath(QuerydslPredicateBuilder.java:167) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.invokeBinding(QuerydslPredicateBuilder.java:136) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPredicate(QuerydslPredicateBuilder.java:111) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:106) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:48) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
Now this query look like it's trying to resolve the propertyPath Person.pets.age. It correctly identifies Person.pets as a ListPath, and then tries to identify CompanyAddress.addressLine1 (which seems correct). The problem is, it tries to use the entity path to get the class, which is the ListPath instead of the QPet:
Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment());
Object value = ReflectionUtils.getField(field, entityPath);
The following query works as expected:
http://localhost:8080/test-app/list?name=Bob
My expectation was that by using ?pets.age=5, the following predicate would be built:
QPerson.person.pets.any().age.eq(5)
Is this currently possible with Spring's QuerydslPredicate support? Or should I manually build the predicates from the query parameters?
Additional Question
As an additional question, is the following possible with QuerydslPredicate. Say I have a firstName and lastName on pet, and I want to run a query with just name=Bob:
http://localhost:8080/test-app/pet/list?name=Bob
I would want the query predicate to be built like this:
final BooleanBuilder petBuilder = new BooleanBuilder();
petBuilder.and(QPet.firstName.equals("Bob").or(QPet.lastName.equals("Bob")));
Is that possible? From looking at the customize method of the QuerydslBinderCustomizer it doesn't seem like it would be, since you need to bind off a field of the Q class. I'm guessing that what I want to do is not supported.
If these aren't possible, then I'll stick with manually creating the predicate, and passing that on to the repository.
You can use QuerydslBinderCustomizer to achieve your purpose. Heres some sample code that can help you out:
public interface PersonRepository extends JpaRepository<Job, Integer>,
QueryDslPredicateExecutor<Person>, QuerydslBinderCustomizer<QJob> {
#Override
public default void customize(final QuerydslBindings bindings, final QPerson person) {
bindings.bind(person.pets.any().name).first((path, value) -> {
return path.eq(value);
});
}
}
I ran into the same error. However I noticed that using the QuerydslAnnotationProcessor plugin (instead of the JPA annotation processor) allows me to query sub collections of entities as expected. You just have to mark all of your entity classes with the #QueryEntity annotation. (The JPA annotation processor automatically generates query classes for #Entity annotated classes.)
In your pom:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/annotations</outputDirectory>
<processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.1.3</version>
</dependency>
</dependencies>
</plugin>
I'm believe I was running into the exception you encountered because I changed from the JPA Annotation Processor to the QuerydslAnnotationProcessor, for some reason I do not recall, and neglected to mark the entity class of the list in question with the #QueryEntity annotation. However I also believe I have another Spring-Data-Rest\JPA backed API that uses the JPA Annotation Processor built in August 2017, and I believe querying sub collections of entities works as expected. I'll be able to confirm that later today, and provide the versions of the relevant dependencies if that is the case. Perhaps this issue has been fixed.

Categories

Resources