Create a custom annotation for JPA #Entity - spring-boot

Whether it is possible to create a custom JPA #Entity in spring boot application.
In my application I need to switch between relation database and Mongo DB with single entity class creation without any duplication of entity class.
I thought of creation my own annotation let say #MyOwn, so that I can inject the annotation based on database need.
#MyOwn will be act as #Entity as well #Document annotation based on database selected.
I am able to custom the #Document entity with #MyOwn annotation.
But for #Entity I am not able to do it.
I am getting below exception.
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.mt.mtamp.employee.entity.Employee
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:583) ~[hibernate-core-5.6.12.Final.jar:5.6.12.Final]
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:85) ~[hibernate-core-5.6.12.Final.jar:5.6.12.Final]
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:75) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:66) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:233) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:182) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:165) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:76) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:325) ~[spring-data-commons-2.7.5.jar:2.7.5]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:323) ~[spring-data-commons-2.7.5.jar:2.7.5]
at org.springframework.data.util.Lazy.getNullable(Lazy.java:231) ~[spring-data-commons-2.7.5.jar:2.7.5]
at org.springframework.data.util.Lazy.get(Lazy.java:115) ~[spring-data-commons-2.7.5.jar:2.7.5]
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:329) ~[spring-data-commons-2.7.5.jar:2.7.5]
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:144) ~[spring-data-jpa-2.7.5.jar:2.7.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.23.jar:5.3.23]
... 49 common frames omitted
Let say
#Entity
public class Employee {
#Id
public Long id;
private String employeeId;
private String employeeNAme;
private String firstname;
private String lastName;
}
Here in this above class I need to replace #Entity with my own annotation let say #MyOwn
The behavior of the #MyOwn should as same as #Entity annotation.
The class should be look like below after my own annotation
#MyOwn
public class Employee {
#Id
public Long id;
private String employeeId;
private String employeeNAme;
private String firstname;
private String lastName;
}
Own Annotation class:

Related

findById causes StackOverflowError but findAll workds fine

I'm having a strange problem with a spring boot endpoint that calls JpaRepository findById(). When ever I send a GET request to the endpoint /v1/goals/{id} a stack overflow error occurs, while GET requests to /v1/goals work fine.
Edit: added the error message to the bottom
Simplified Controller class:
#RestController
public class GoalController {
private final GoalServiceImpl service;
#Autowired
GoalController(GoalServiceImpl service) { this.service = service; }
#GetMapping("/v1/goals")
ResponseEntity<List<Goal>> allGoals() { return new ResponseEntity<>(service.getGoals(), HttpStatus.OK); }
#GetMapping("/v1/goals/{id}")
ResponseEntity<String> singleGoal(#PathVariable Long id) {
return new ResponseEntity<>(service.getGoalById(id).toString(), HttpStatus.OK);
}
}
Simplified Service class:
#Service
public class GoalServiceImpl implements GoalService {
private final GoalRepository repository;
#Autowired
public GoalServiceImpl(GoalRepository repository) { this.repository = repository; }
public Goal getGoalById(Long id) {
return repository
.findById(id)
.orElseThrow(() -> new GoalNotFoundException(id));
}
public List<Goal> getGoals() { return repository.findAll(); }
}
Simplified Entity class:
#NoArgsConstructor
#Data
#Entity
public class Goal {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long goalId;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id")
#JsonIgnore
private User user;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "goal")
#JsonIgnoreProperties(value = "goal")
private List<Milestone> milestones;
}
Simplified Milestone Entity:
#NoArgsConstructor
#Data
#Entity
public class Milestone {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long milestone_id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
#JsonIgnore
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "goal_id")
private Goal goal;
Simplified User Entity:
#NoArgsConstructor
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userId;
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
#JsonIgnoreProperties(value = "user")
private List<Goal> goals;
}
Error message:
java.lang.StackOverflowError: null
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:56) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at com.motivate.api.user.User$HibernateProxy$KaIclPZ9.toString(Unknown Source) ~[classes/:na]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at com.motivate.api.goal.Goal.toString(Goal.java:16) ~[classes/:na]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na]
at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:473) ~[na:na]
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:622) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na]
at com.motivate.api.user.User.toString(User.java:11) ~[classes/:na]
at jdk.internal.reflect.GeneratedMethodAccessor58.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:56) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at com.motivate.api.user.User$HibernateProxy$KaIclPZ9.toString(Unknown Source) ~[classes/:na]
Solution
provided by Chris and PaulD in the comments:
#GetMapping("/v1/goals/{id}")
ResponseEntity<Goal> singleGoal(#PathVariable Long id) {
return new ResponseEntity<>(service.getGoalById(id), HttpStatus.OK);
}
Remove the toString() as that causes the whole object to be serialised by lombok which doesn't take into account the jackson annotations. Change ResponseEntity to be of type Goal and pass in the whole entity.

Are there #MappedSuperclass in Spring Reactive Data (R2DBC)

I have a super Entity class like this:
#Getter
#Setter
#NoArgsConstructor
public class GenericEntity {
#Id
private Long id;
#JsonIgnore
#CreatedBy
private Long createdBy;
#JsonIgnore
#CreatedDate
private Long createdDate;
#JsonIgnore
#LastModifiedBy
private Long updatedBy;
#JsonIgnore
#LastModifiedDate
private Long updatedDate;
#JsonIgnore
#Version
private Integer version = 0;
}
and a Role class extends from GenericEntity like this:
#Getter
#Setter
#NoArgsConstructor
public class Role extends GenericEntity {
private String name;
private String desc;
private Integer sort;
}
And after that I have interface RoleRepo like this:
#Repository
public interface RoleRepo extends ReactiveCrudRepository<Role, Long>;
In Router function, I have 2 handler methods
private Mono<ServerResponse> findAllHandler(ServerRequest request) {
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(roleRepo.findAll(), Role.class);
}
private Mono<ServerResponse> saveOrUpdateHandler(ServerRequest request) {
return ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(request.bodyToMono(Role.class).flatMap(role -> {
return roleRepo.save(role);
}), Role.class);
}
The method findAllHandler works fine, but the saveOrUpdateHandler throw exception like this:
java.lang.IllegalStateException: Required identifier property not found for class org.sky.entity.system.Role!
at org.springframework.data.mapping.PersistentEntity.getRequiredIdProperty(PersistentEntity.java:105) ~[spring-data-commons-2.2.0.M2.jar:2.2.0.M2]
at org.springframework.data.r2dbc.function.convert.MappingR2dbcConverter.lambda$populateIdIfNecessary$0(MappingR2dbcConverter.java:85) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
But when I move
#Id
private Long id;
from GenericEntity class to Role class, the two methods work fine.
Are there any Annations #MappedSuperclass/JPA in Spring Reactive Data like that
I wish the id field in GenericEntity for all extends class
Thanks for your help
Sorry, my English so bad
I had a similar problem and after some search, I didn't find an answer to your question, so I test it by writing code and the answer is spring data R2DBC doesn't need #Mappedsuperclass. it aggregates Role class properties with Generic class properties and then inserts all into the role table without the need to use any annotation.

Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: jpa+springboot

model classes
#Entity
#Table(name="RequisitionRequest")
public class Requisition {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String requestedPerson;
private String contactDetails;
private String appliedDate;
private String branch;
private String department;
private String status;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "RequisitionRequest")
private List<Nationality> nationality;
//getters and setters
}
class Nationality
#Entity
#Table(name="nationality")
public class Nationality {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String nationality;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "nationality_id", nullable = false)
private Requisition requisition;
//getters and setters
}
repository classes
#Repository
public interface NationalityRepository extends JpaRepository<Nationality,Long>{
}
#Repository
public interface RequisitionRepository extends JpaRepository<Requisition,Long> {
}
service class
#Service
public class RequisitionServiceImpl implements RequisitionService {
#Autowired
RequisitionRepository requisitionRepository;
#Override
public void save(Requisition requisition) {
requisitionRepository.save(requisition);
}
}
controller class
#RestController
public class RequisitionController {
#Autowired
RequisitionService requisitionService;
#Autowired
RequisitionRepository requisitionRepository;
#PostMapping("/requisition/")
#CacheEvict(value = "requisitions", allEntries=true)
public ResponseEntity<Requisition> addRequisition(#RequestBody Requisition requisition) {
System.out.print(requisition);
Requisition requisitionR = new Requisition();
Requisition response = new Requisition();
requisitionR.setBranch(requisition.getBranch());
requisitionR.setDepartment(requisition.getDepartment());
requisitionR.setExpectedDateofJoin(requisition.getExpectedDateofJoin());
//requisitionR.setNationality(requisition.getNationality());
requisitionRepository.save(requisitionR);
return new ResponseEntity<Requisition>(response, HttpStatus.OK);
}
i am learning spring boot,just writing sample program to learn spring boot+jpa+one-to-many relation.when execute this above code getting error like
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: officekitRecruitment.model.Nationality.RequisitionRequest in officekitRecruitment.model.Requisition.nationality
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:769) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:719) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1655) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1623) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:278) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
I tried the solutions already mentionned in this site but it doesn't work for me.
Can anybody helps me to resolve this issue
You should refer to the name of the Field in mapped entity on your MappedBy parameter.
Like that
mappedBy = "requisition"

java.lang.IllegalArgumentException: IN (1): [IsIn, In]is not supported for redis query derivation - Redis

It looks like Spring Data Redis does not support the IsIn, In queries yet. I am not sure if there is any other of way of doing it as its very basic queries. It was either to be happned using Spring Data Redis or RestTemplate.
java.lang.IllegalArgumentException: IN (1): [IsIn, In]is not supported for redis query derivation
at org.springframework.data.redis.repository.query.RedisQueryCreator.from(RedisQueryCreator.java:67)
at org.springframework.data.redis.repository.query.RedisQueryCreator.create(RedisQueryCreator.java:53)
at org.springframework.data.redis.repository.query.RedisQueryCreator.create(RedisQueryCreator.java:41)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:119)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:95)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:81)
at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.createQuery(KeyValuePartTreeQuery.java:211)
at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.prepareQuery(KeyValuePartTreeQuery.java:148)
at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.execute(KeyValuePartTreeQuery.java:106)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:602)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:590)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy76.findByNameIn(Unknown Source)
at com.mastercard.customer.program.UserGroupTest.test(UserGroupTest.java:58)
Group.java
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
#RedisHash("groups")
public class Group {
#Id
private Long groupId;
private String name;
}
User.java
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
#RedisHash("users")
public class User {
#Id
private Long userId;
private String name;
}
UserGroupTest.java
#Log4j
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class UserGroupTest extends RepositoryTestSupport{
#Autowired UserRepository userRepository;
#Autowired GroupRepository groupRepository;
#Before
public void setUp() {
User user1 = User.builder().userId(1L).name("John").build();
User user2 = User.builder().userId(2L).name("Sally").build();
User user3 = User.builder().userId(3L).name("Chris").build();
Group group1 = Group.builder().groupId(1L).name("Nature-Group").build();
Group group2 = Group.builder().groupId(2L).name("Music-Group").build();
Group group3 = Group.builder().groupId(3L).name("Sports-Group").build();
userRepository.save(user1);
userRepository.save(user2);
userRepository.save(user3);
groupRepository.save(group1);
groupRepository.save(group2);
groupRepository.save(group3);
}
#Test
public void test() {
List<User> users = userRepository.findByNameIn(Arrays.asList("John", "Sally"));
System.out.println(users.size());
}
}
Spring Data Redis 2.1.2 (recent version at the time of writing) does not support In queries through query derivation. There is a ticket to support the In query type.

Spring boot circular dependency issue when using gh-cache

I am using spring boot and i want to cache some data here is my entity and repository classes
Places entity class:
#Entity
#Table(name = "PLACE_MASTER")
#Getter
#Setter
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Component
public class PlaceMaster extends BaseEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PLACE_MASTER_ID")
#JsonView(View.Place.class)
private long placeMasterId;
#Column(name = "PLACE_NAME")
#JsonView(View.Place.class)
private String placeName;
#Column(name = "ALT_PLACE_NAME")
private String alternatePlaceName;
#Column(name = "PINCODE")
private String pinCode;
#Column(name="DISTRICT_NAME")
private String districtName;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn(name = "STATE_MASTER_ID")
private StateMaster stateMaster;
}
state entity class:
#Entity
#Table(name = "STATE_MASTER")
#Getter
#Setter
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
//#ToString
public class StateMaster extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "STATE_MASTER_ID")
private long stateMasterId;
#Column(name = "STATE_NAME")
private String stateName;
#Column(name = "STATE_CODE")
private String stateCode;
#OneToMany(mappedBy = "stateMaster", cascade = CascadeType.ALL)
private List<PlaceMaster> placeMaster = new ArrayList<PlaceMaster>();
}
public interface PlaceMasterRepository extends JpaRepository<PlaceMaster, Long> {
#Override
#Cacheable("places")
public List<PlaceMaster> findAll();
}
bootstrap application class:
#SpringBootApplication
#EnableCaching
#EnableAsync
#EnableTransactionManagement
public class myApplication {
#Autowired
private PlaceMasterRepository placeMasterRepository;
public static void main(String[] args) throws Throwable {
SpringApplication.run(GoyaanaApplication.class, args);
}
}
when i run the the spring boot application i am getting the below error. Is it due to biderectional dependency. Please help
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'entityManagerFactory': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
It is generally best to avoid circular dependencies between your bean, e.g. by moving #EnableCaching, #EnableAsync, #EnableTransactionManagement to separate #Configuration class.
In cases where it is not possible, you can get around this by adding a #Lazy annotation on the #Autowired bean. This will create a lazy proxy that will get resolved at runtime. This comes with a caveat - you can't be sure at the start of the application if it is wired up correctly.
#Lazy
#Autowired
private PlaceMasterRepository placeMasterRepository;
For details see Spring documentation.

Resources