Grails 3.x: Re-using JPA/Hibernate Domain classes: Domain class not found - spring

I have a Spring 4.1.0 back-end application with domain classes annotated in JPA (w/Hibernate 4.3.5 as the persistence provider) using Maven as the build tool. I now want to add a web front-end component to this app and have decided to use Grails 3.x as my web framework. I want to re-use my existing JPA annotated domain classes with Grails and then use generate-all to create controllers and views for each domain model. My first milestone goal is to get basic CRUD functionality on the old domain models from this web app.
Following the information I found in the Grails documentation and in some older blog posts as well as some slightly related SO posts, I created a new Grails project and packaged up my existing project as a jar and installed it (I ran mvn install -DskipTests to be exact) and then added it to build.gradle (actually I just want to have one project in the end, but I thought I'd try it this way first because I don't want to wrestle with having Maven and Gradle in the same project yet):
repositories {
...
mavenLocal()
...
}
dependencies {
...
compile "com.my-company:my-spring-app:1.0.0.CI-SNAPSHOT"
...
}
No warnings or errors from IntelliJ IDEA 14 so far. Then I created the grails-app/conf/hibernate/hibernate.cfg.xml file and tried putting just one of my old JPA annotated entities in it for now:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping package="com.my-company.my-spring-app.domain" />
<mapping class="com.my-company.my-spring-app.domain.City" />
</session-factory>
</hibernate-configuration>
No complaints from the IDE here either.
The City.java entity class looks something like:
package com.my-company.my-spring-app.domain;
import javax.persistence.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
/**
* City generated by hbm2java
*/
#Entity
#Table(name = "city",
schema = "public",
uniqueConstraints = #UniqueConstraint(columnNames = "name"))
public class City implements java.io.Serializable {
private static final long serialVersionUID = 4674557242772722625L;
#Id
#SequenceGenerator(name = "city_gen",
schema = "public",
sequenceName = "city_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "city_gen")
#Column(name = "id",
unique = true,
nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "countryid",
nullable = false)
// #JoinColumn(name = "country_id", nullable = false)
private Country country;
#Column(name = "name",
unique = true,
length = 200)
private String name;
...
}
Then I jumped into the Grails console and tried to generate the controller and views for the City domain class:
grails generate-all com.my-company.my-spring-app.domain.City
But I just get a Domain class not found error:
| Error domain class not found for name com.my-company.my-spring-app.domain.City
I quickly created a Grails 2.5.0 app, put the my-spring-app.jar in lib/ and tried this again to see if it was an issue with the 'bleeding edge' Grails 3.0.1 but got the same result.
Does anyone know what's going on here? How can I re-use my old JPA domain classes with Grails 3.x so that I can stay DRY with only one set of domain entities?

I resolved a similar issue by putting hibernate.cfg.xml in grails-app/conf (not inside a hibernate subdirectory) as described in mapping-with-hibernate-annotations-in-grails-3-0-1.

Related

Spring: map entity to a scheme

Im trying to create a Spring-Boot Data JPA-Application with Entities based in multiple schemes using an oracle Database.s
I have two Schemes, scheme_a and scheme_b. The DDL User for scheme_a is scheme_a, the DDL user for scheme_b is scheme_b. Both DDL user will be used by liquibase to create my initial table structure.
My application has two entities:
Entity foo is managed by my application and should be based in scheme scheme_a.
Entity bar is managed by a third party library and should be based in scheme scheme_b.
My application has a user app_user, with CRUD-rights on all tables and sequences located in scheme_a and scheme_b. This user is my DMA-user
My plan was to use Spring-Data-Jpa to connect with the database using the app-user user. This user should be able to work with all entities in scheme_a and scheme_b.
Example of Entity foo (bar has identical structure):
#Entity
#Table(name = "T_FOO")
public class FooEntity {
public FooEntity () { // no-args c-tor for hibernate
}
#Id
#Column(name = "foo_id")
#SequenceGenerator(name = "sequence_foo_id", sequenceName = "sequence_foo_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence_foo_id")
private long id;
#Column(name = "foo_name")
private String name;
}
Every entity has its own Repository:
public interface FooRepo extends CrudRepository<FooEntity, Long> {
...
}
My configuration:
#Configuration
#EnableJpaRepositories(basePackageClasses = {FooRepo.class, BARRepo.class})
#EntityScan(basePackageClasses = {FooEntity.class, BarEntity.class})
public class AppDBConfig {
...
}
But every time my application tries to start it is not able to locate the table T_FOO based in scheme_a.
I am not able to extend the #Table-Annotation with corresponding scheme. Does any one know a way to solve this problem? Is it possible to create something like a "scheme-table" to tell hibernate in which scheme which entity is located, like
var schemeMap = new HashMap<Class, String>();
schemeMap.put(FooEntity.class, scheme_a);
schemeMap.put(BarEntity.class, scheme_b);
Greetings from Germany!
You need to specify the schema in the #Table annotation.
#Entity
#Table(name = "T_FOO", schema = "scheme_a")
public static void FooEntity {
// ..
Since you don't seem to be allowed to change the annotation on FooEntity I see a couple of options available to you:
If you can change the annotation on BarEntity but not on FooEntity you can make the schema of FooEntity your default schema and set the schema for BarEntity in the annotation.
If you can't change either entity but have some control over the database you can create a view or synonym in your main schema that mirrors T_FOO.
You can also configure Hibernate with XML and specify the schema there.
You could decompile the class file containing FooEntity, add the required annotation and compile it again.
You probably could use ByteBuddy or a similar tool to do that at runtime during startup of your application.

Spring Boot Hibernate not picking up use-new-id-generator-mappings property

I'm upgrading my project to Spring Boot 2.1.18 that uses Hibernate 5.3.18.
Previously, my entity looked like thus and would use the SequenceHiLoGenerator:
#Entity
#Table(name = "group_link")
#SequenceGenerator(name = "group_link_seq", sequenceName = "group_link_seq")
public class GroupLinkEntity extends BaseObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "group_link_seq")
#Column(name = "group_link_id", unique = true, nullable = false)
private Long id
}
Now, by default in Hibernate 5, it uses the SequenceStyleGenerator which causes constraint violations because my increment size is 1 and the default allocationSize is 50.
The suggested thing to do to maintain compatibility is to set this property:
spring.jpa.properties.hibernate.use-new-id-generator-mappings: false
I do so but it does not seem to take, because the SequenceStyleGenerator is still used. From my understanding, this should cause it to use the SequenceHiLoGenerator. Is this incorrect?
However, if I modify the entity to look like the below it works as expected, replicating the previous functionality I had.
#Entity
#Table(name = "group_link")
#GenericGenerator(
name = "group_link_seq",
strategy = "org.hibernate.id.SequenceHiLoGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "group_link_seq"),
}
)
public class GroupLinkEntity extends BaseObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "group_link_seq")
#Column(name = "group_link_id", unique = true, nullable = false)
private Long id;
}
So, it would seem the property is not being taken somehow and I'm looking to figure out why that is. I see it show up in my JpaProperties bean. If I change other properties, like my dialect, I can see that they are taking effect.
Could anyone point me to the code that actually reads that property and makes a decision on what generator to use or point out some obvious error I'm making here?
As it's stated in the documentation:
You need to ensure that names defined under spring.jpa.properties.* exactly match those expected by your JPA provider. Spring Boot will not attempt any kind of relaxed binding for these entries.
For example, if you want to configure Hibernate’s batch size you must use spring.jpa.properties.hibernate.jdbc.batch_size. If you use other forms, such as batchSize or batch-size, Hibernate will not apply the setting.
So, for your case you should use:
spring.jpa.properties.hibernate.id.new_generator_mappings: false
See also this part of hibernate documentation.

Spring Boot repository save does not work (only shows a select)

I'm facing for hours with a strange proceeding in Spring Boot when try to save a mapped entity.
The entity class with a composite key that must all be set by the user is as follows:
package model
import javax.persistence.*
#Entity
#Table(name = 'MY_TABLE')
#IdClass(MyIdClass.class)
class MyClass implements Serializable{
#Id
#Column(name = "MY_COLUMN_1")
Long column1
#Id
#Column(name = "MY_COLUMN_2")
Long column2
#Id
#Column(name = "MY_COLUMN_3")
String column3
#Id
#Column(name = "MY_COLUMN_4")
Date date1
#Column(name = "MY_COLUMN_5")
Date date2
#Column(name = "MY_COLUMN_6")
BigDecimal column6
}
#Embeddable
class MyIdClass implements Serializable{
Long column1
Long column2
String column3
Date date1;
}
The corresponding repository is:
package repository
import org.springframework.data.repository.CrudRepository
interface MyRepository extends CrudRepository<MyClass, Long>{
}
My service is:
package service
import model.MyClass
import repository.MyRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
#Service
class MyService {
#Autowired
MyRepository repository
void save(MyClass myClass) {
repository.save(myClass)
}
}
My controller mounts a MyClass object with all data set, including the composite key. When it calls the service save method the object is not inserted in the database. I saw the logs and checked that there is a SELECT in MY_TABLE instead of INSERT. I tried not to inform the composite key in the object and then the save method did an INSERT with error due to null values in the primary key.
I really don't understand why the insertion is not done when the composite key has values. How can I solve it?
I've already tried with #Transactional in service class and didn't work. I didn't do any Transaction configuration in the project since Spring Boot delivers it as default.
Thanks.
It seems you are using MyIdClass as the Id for MyClass. So, the Repository should be:
interface MyRepository extends CrudRepository<MyClass, MyIdClass>{
}
Hope this help.
I take your code sample and tried it on a sample Spring Boot project, where I was able to save to H2 DB (In memory) with #Embeddable & #EmbeddedId annotations. If you would like to verify, you can clone the GitHub repo and run the BootJpaApplication.java as a Java Application.
After execution access the H2 console with the below link from local where table details can be verified.
http://localhost:8080/h2-console
https://github.com/sujittripathy/springboot-sample.git
Hope the detail helps:)

Moving Spring Boot 1.3 to 1.4, Hibernate 4 to 5, Pascal Case Issues

I created a Spring Boot 1.3.5 POC with Spring Data JPA and Hibernate (4.3.11.Final in this version of Spring Boot). My backend database is Microsoft SQL Server, and our standard naming convention for database objects is pascal case (e.g. MySchema.MyTable.MyColumn). I used the javax.persistence.Table and javax.persistence.Column annotations to set the names, and added spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy to my application.properties file.
Everything worked perfectly. I even updated to Spring Boot 1.3.6 with no issues.
Now I moved to Spring Boot 1.4.0.RELEASE which uses Hibernate 5.0.9.Final, and the spring.jpa.hibernate.naming-strategy property is deprecated in favor of spring.jpa.hibernate.naming.strategy. I changed that property name, but left the EJB3NamingStrategy value. I also changed the other deprecated elements:
org.springframework.boot.orm.jpa.EntityScan to org.springframework.boot.autoconfigure.domain.EntityScan
org.springframework.boot.context.web.SpringBootServletInitializer to org.springframework.boot.web.support.SpringBootServletInitializer
org.springframework.boot.test.SpringApplicationConfiguration to org.springframework.boot.test.context.SpringBootTest (in my test classes)
Now the generated SQL uses the default camel case to underscore naming convention and not the pascal case that I had with EJB3NamingStrategy.
//application.properties
spring.data.jpa.repositories.enabled=true
spring.data.solr.repositories.enabled=false
spring.data.mongodb.repositories.enabled=false
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.EJB3NamingStrategy
#spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy
//hibernate.properties
hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
hibernate.format_sql=true
//Principal.java
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import org.hibernate.envers.AuditTable;
import org.hibernate.envers.Audited;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Entity
#Table(name="Principal", schema="Security")
#Audited
#AuditTable(value = "Principal", schema = "Audit")
public class Principal {
private static final Logger LOG = LoggerFactory.getLogger(Principal.class);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Id",
nullable = false)
private Long id;
#Column(name = "Username",
nullable = false,
unique = true)
#Size(min = 1, max = 64)
private String name;
#Column(name = "FirstName",
nullable = false)
#Size(min = 1, max = 64)
private String firstName;
#Column(name = "LastName",
nullable = false)
#Size(min = 1, max = 128)
private String lastName;
#Column(name = "IsEnabled",
nullable = false)
private boolean enabled;
//getters/setters omitted for brevity
}
orignal console output:
Hibernate:
select
principal0_.Id as Id1_8_,
principal0_.IsEnabled as IsEnable2_8_,
principal0_.FirstName as FirstNam3_8_,
principal0_.LastName as LastName4_8_,
principal0_.Username as Username5_8_
from
Security.Principal principal0_
where
principal0_.Username=?
new console output:
Hibernate:
select
principal0_.id as id1_7_,
principal0_.is_enabled as is_enabl2_7_,
principal0_.first_name as first_na3_7_,
principal0_.last_name as last_nam4_7_,
principal0_.username as username5_7_
from
security.principal principal0_
where
principal0_.username=?
2016-08-05 09:19:22.751 WARN 5032 --- [ XNIO-2 task-8] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 207, SQLState: S0001
2016-08-05 09:19:22.751 ERROR 5032 --- [ XNIO-2 task-8] o.h.engine.jdbc.spi.SqlExceptionHelper : Invalid column name 'is_enabled'.
2016-08-05 09:19:22.768 ERROR 5032 --- [ XNIO-2 task-8] io.undertow.request : UT005023: Exception handling request to /springbootsecurity/login
I've searched extensively, and found references to ImplicitNamingStrategy and PhysicalNamingStrategy; but plugging those in don't seem to work and is probably not the correct approach. I've also seen references to creating my own NamingStrategy. Is that the route I must take?
Is there a different setting for Hibernate 5 that will use the exact name I provide in the #Table and #Column annotations?
Is there a problem with the way I am defining the annotations?
I would like to say I ended up posting a silly question, but every direction I went talked about creating a custom naming strategy. The answer in my case, however, was simply using Hibernate's PhysicalNamingStrategyStandardImpl.
Added to application.properties:
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
From my naive analysis, I'm assuming this works because I am using the #Table and #Column annotations. The PhysicalNamingStrategyStandardImpl appears to simply use the name in those annotations as the database object name.
So my Hibernate generated query is once again formatted as:
Hibernate:
select
principal0_.Id as Id1_7_,
principal0_.IsEnabled as IsEnable2_7_,
principal0_.FirstName as FirstNam3_7_,
principal0_.LastName as LastName4_7_,
principal0_.Username as Username5_7_
from
Security.Principal principal0_
where
principal0_.Username=?
Reading #AmanTuladhar's link and this link from that post is where it eventually clicked for me. Thanks!
this is really a nice thread , for a beginner - who are migrating from spring boot 1.3 to 1.4 - below link contains all the stranded changes required , it also list all the deprecated options and contains some examples as well .
It gives overview about almost everything that you can use with application. For ex- Hibernate , Log4j , Junit/Mockito , Integration and so on . Please follow below link
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes

Spring Data JPA with MVC repository

I am building a Spring repository for some JPA-annotated entities. I have created a repository:
public interface AppRepository extends PagingAndSortingRepository<App, String>
{
}
The App class looks as follows:
#Entity
public class App implements Serializable
{
#Id
private String appId;
#OneToMany(mappedBy = "app")
private List<AgentUser> agentusers;
#OneToMany(mappedBy = "app")
private List<AppFacet> appfacets;
// getters and setters go here
}
where the AgentUser and the AppFacet hold a reference property called app towards an App object. In the AgentUser class, I have changed the RestResource rel:
#Entity
public class AgentUser
{
...
#ManyToOne
#JoinColumn(name = "AppId")
#RestResource(rel = "agentUserToApp", exported = false)
private App app;
// other properties go here
}
I am getting the following error message while querying the /apps path:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: org.springframework.hateoas.PagedResources["_embedded"]);
Do you know what could be causing it? Please note that I only have one App object in a database, for testing purposes and no other kind of object.
Update
The trace is:
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:677)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2240)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:231)
org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:161)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:167)
And after that, a lot of:
org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:352)
org.springframework.data.rest.webmvc.mapping.LinkCollectingAssociationHandler.doWithAssociation(LinkCollectingAssociationHandler.java:101)
The problem resides in that, whenever you have links to some entities, you must implement a repository for that entity too, in order to generate the proper links.

Resources