How to implement database inheritance in Spring Data JPA with MapperSuperClass? - spring-data-jpa

I'm trying out database inheritance of type JOINED in Spring Data JPA, referring to this article. This worked fine. But I've to implement MappedSuperClass in my project. I've implemented in the following way:
Base.java
#MappedSuperclass
public abstract class Base {
public abstract Long getId();
public abstract void setId(Long id);
public abstract String getFirstName();
public abstract void setFirstName(String firstName);
}
BaseImpl.java
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class BaseImpl extends Base {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
...
}
Super1.java
#MappedSuperclass
public abstract class Super1 extends BaseImpl {
public abstract String getSuperName();
public abstract void setSuperName(String guideName);
}
Super1Impl.java
#Entity
public class Super1Impl extends Super1 {
private String superName;
...
}
BaseBaseRepository.java
#NoRepositoryBean
public interface BaseBaseRepository<T extends Base> extends JpaRepository<T, Long> { }
BaseRepository.java
#NoRepositoryBean
public interface BaseRepository<T extends Base> extends BaseBaseRepository<Base> { }
BaseRepositoryImpl.java
#Transactional
public interface BaseRepositoryImpl extends BaseRepository<BaseImpl> { }
Super1Repository.java
#NoRepositoryBean
public interface Super1Repository<T extends Super1> extends BaseBaseRepository<Super1> { }
Super1RepositoryImpl.java
#Transactional
public interface Super1RepositoryImpl extends Super1Repository<Super1Impl> { }
I'm trying to save a Super1 object in a test case:
#Test
public void contextLoads() {
Super1 super1 = new Super1Impl();
super1.setSuperName("guide1");
super1.setFirstName("Mamatha");
super1.setEmail("jhhj");
super1.setLastName("kkjkjhjk");
super1.setPassword("jhjjh");
super1.setPhoneNumber("76876876");
System.out.println(super1Repository.save(super1));
}
But I'm getting the following error:
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'baseRepositoryImpl':
Invocation of init method failed; nested exception is java.lang.IllegalArgumentException:
This class [class com.example.entity.Base] does not define an IdClass
.....
Caused by: java.lang.IllegalArgumentException: This class [class com.example.entity.Base] does not define an IdClass
.......
Tried out #PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id") in Super1Impl, but still getting the same error.

The error is caused by incorrect repository interface declarations.
BaseRepository<T extends Base> extends BaseBaseRepository<Base>
should be
BaseRepository<T extends Base> extends BaseBaseRepository<T>
and
Super1Repository<T extends Super1> extends BaseBaseRepository<Super1>
should be
Super1Repository<T extends Super1> extends BaseBaseRepository<T>
As currently declared, BaseBaseRepository<Base> means a repository of Base objects and Base does not have an #Id field, hence the error.

Related

Spring Data Jpa Query methods are not invoking the repositoryBaseClass

I have a repository base class as defined below.
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
}
public class BaseRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {
public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInfo, EntityManager entityMgr) {
super(entityInfo, entityMgr);
}
// ...
}
#Configuration
#EnableJpaRepositories(basePackages = "org.example",
repositoryBaseClass = BaseRepositoryImpl.class)
public class BaseConfig {
// additional JPA Configuration
}
I have defined a business repository class and a query method as seen below.
#Repository
public interface CarRepository extends BaseRepository<Car, Long> {
#Query("SELECT c FROM Car c Where active = 1")
List<Car> findAllActiveCars();
}
I have a test class which invokes the findAllActiveCars(). I am getting the expected results. But, that query method is not invoking any of the methods in BaseRepository class. How to customize the return values of the query methods?
You didn't show the methods that you did implement, so it is not clear why they don't get called, but since you want to decrypt entity fields, consider listening to JPAs entity lifecycle events. #PostLoad should be able to do the trick.
https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/listeners.html

Eclipse cannot see beyond Beans' models' fields from AbstractBean/AbstractEntity in Facelets

I am running jee-2019-06 version of Eclipse. Here is my Model-Bean-Facade structure:
I am not including getters/setters for brevity.
My Identifiable:
/** Identifiable interface for Entities; used for DAO - Service transitions. */
public interface Identifiable<T extends Serializable> extends Serializable {
public T getId(); // identifiable field
public String getTitle(); // user friendly name (maybe different from actual entity's name)
public String getName(); // every entity has a name
public String getDescription(); // every entity should have a description
}
My Abstract Bean:
public abstract class AbstractBean<T extends Identifiable<?>> {
protected final transient Logger log = Logger.getLogger(this.getClass());
private final Class<T> clazz;
private T model;
public AbstractBean(final Class<T> clazz) {
this.clazz = clazz;
}
protected T createInstance() {
try {
return this.clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
this.log.error("[" + this.getClass().getSimpleName() + ".createInstance()] : Error : {} {}", e.getMessage(), e);
return null;
}
}
protected AbstractFacade<T> getFacade() {
return null;
}
}
My Abstract Facade:
#Transactional
public abstract class AbstractFacade<T extends Identifiable<?>> {
protected final transient Logger log = Logger.getLogger(this.getClass());
protected final Class<T> clazz;
public AbstractFacade(final Class<T> clazz) {
this.clazz = clazz;
}
}
My Bean:
#Named
#ViewScoped
public class CarBean extends AbstractBean<Car> {
#Inject
private CarFacade facade;
public CarBean(){
super(Car.class);
}
#Override
public CarFacade getFacade() {
return this.facade;
}
}
My AbstractEntity:
#MappedSuperclass
public abstract class AbstractEntity implements Identifiable<Integer> {
private Integer id;
private String name;
private String description;
public AbstractEntity() {
}
}
My Entity:
public class Car extends AbstractEntity {
public Car() {
}
}
I have no problems in showing the value to the user.
I have problems in validation and hyperlink in Eclipse:
<h:outputText value="#{carBean.model.name}" />
Facelet validator cannot validate name of model. It yellow underlines name. Also, I cannot Ctrl + click to activate hyperlink on name.
I saw on another developer's eclipse that both of my problems were not issues at all. I compared all the tools installed in both Eclipses and could not find anything relevant.
My question: what tools do I have to install or what settings/adjustments am I missing?
Please note: I do not want to disable the validator and I want to be able to hyperlink fields in facelet so that I will access the field using Ctrl + click.
Thank you.

How is entities inheritance implemented in Spring data mongodb

I've two entities Person, Employee and Employee1. I want to implement entities inheritance in Spring Data MongoDB. Like in Spring Data JPA, what are the equivalent annotations for #Inheritance and #PrimaryKeyJoinColumn in Spring Data MongoDB. Right now, I've implemented something like this:
interface Person {
String getId();
void setId(String id);
String getName();
void getName(String name);
}
#Document(collection = "person")
class PersonImpl implements Person {
#Id
String id;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
interface Employee extends Person {
int getNumberOfDependents();
void getNumberOfDependents(int numberOfDependents);
}
#Document(collection = "employee")
class EmployeeImpl extends PersonImpl implements Employee {
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
interface Employee1 extends Person {
int getNumberOfDependents();
void getNumberOfDependents(int numberOfDependents);
}
#Document(collection = "employee1")
class Employee1Impl extends PersonImpl implements Employee1 {
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
Repository structure:
public interface PersonRepository extends MongoRepository<Person, String> {
}
public interface EmployeeRepository extends MongoRepository<Employee, String> {
}
public interface Employee1Repository extends MongoRepository<Employee1, String> {
}
I'm saving the Person object first and then taking the ID of it and creating an Employee object with the same ID and saving it. This creates new object and hence I'm losing all the Person object stuff.
I also feel that I've to get the NoRepositoryBean implemented also.
I'm confused. Please help.
Here is one approach:
#Document(collection = "person")
class Person {
#Id
String id;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
Note that the collection field refers to "person" and not to "employee"
#Document(collection = "person")
class Employee extends Person {
String jobTitle;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
In this method you do not need to create a repository for each derived class
#Repository
public interface PersonRepository extends MongoRepository<Person, String> {}
Code example:
#Autowired
private PersonRepository personRepo;
public void test() {
Employee employee = new Employee("1", "teacher")
personRepo.save(employee)
Optional<Person> optionalPerson = personRepo.findById("1");
Employee employeeFromDb;
if (optionalPerson.isPresent()) {
employeeFromDb = (Employee)optionalPerson.get()
}
else {
// could not find in db
}
}
if you want to find all employees you should have a methode on MongoRepository
called
List<Employee> findAll();

#Autowired does not work in SimpleJpaRepository extension

When trying to override SimpleJpaRepository, adding other beans via #Autowired does not work. How can beans be injected in this case? Here is an implementation:
public class BaseDAO<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID>
implements IDAO<T, ID> {
#Autowired
private SomeBean someBean; // NULL!
}
Instances of BaseDAO are not Spring-managed beans in themselves and therefore autowiring through #Autowired will not work out-of-the-box. Dependencies therefore need to be injected into BaseDAO instances.
Step 1: Have a Spring ApplicationContext available somewhere
#Component
class SpringContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(final ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public static ApplicationContext getContext() { return CONTEXT; }
}
This will be required to autowire the dependencies for the custom repository implementation at the time of repository creation.
Step 2: Extend SimpleJpaRepository
class BaseDAO<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> {
#Autowired
private Dependency dependency;
}
Step 3: Autowire dependencies through a JpaRepositoryFactoryBean
class ExtendedJPARepositoryFactoryBean<R extends JpaRepository<T, ID>, T, ID extends Serializable>
extends JpaRepositoryFactoryBean<R, T, ID> {
private static class ExtendedJPARepositoryFactory<T, ID extends Serializable> extends JpaRepositoryFactory {
public ExtendedJPARepositoryFactory(final EntityManager entityManager) {
super(entityManager);
}
protected Class<?> getRepositoryBaseClass(final RepositoryMetadata metadata) {
return isQueryDslExecutor(metadata.getRepositoryInterface())
? QueryDSLJPARepository.class
// Let your implementation be used instead of SimpleJpaRepository.
: BaseDAO.class;
}
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
RepositoryInformation information, EntityManager entityManager) {
// Let the base class create the repository.
final SimpleJpaRepository<?, ?> repository = super.getTargetRepository(information, entityManager);
// Autowire dependencies, as needed.
SpringContext.getContext()
.getAutowireCapableBeanFactory()
.autowireBean(repository);
// Return the fully set up repository.
return repository;
}
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new ExtendedJPARepositoryFactory(entityManager);
}
}
Step 4a: Configure Spring Data JPA to use your factory bean (XML configuration)
<jpa:repositories base-package="org.example.jpa"
factory-class="org.example.jpa.ExtendedJPARepositoryFactoryBean"/>
Step 4b: Configure Spring Data JPA to use your factory bean (Java configuration)
#EnableJpaRepositories(repositoryFactoryBeanClass = ExtendedJPARepositoryFactoryBean.class)
In order to let Spring know that it needs to inject something inside your DAO you need to annotate it with #Component.
You can read more about this here: http://docs.spring.io/autorepo/docs/spring-boot/current/reference/html/using-boot-spring-beans-and-dependency-injection.html.

Morphia: inheritance not handled properly?

I have a class that implements an interface. Why are the arraylist contents not stored in the database? Here is some code to illustrate the problem.
The class
#Entity
public class MyClass implements MyInterface {
#Id
#Indexed
public String id;
public String someField;
public MyClass(String id, String someField){
this.id = id;
this.someField = someField;
}
}
The interface
public interface MyInterface {
#Embedded
public List<String> mylist = new ArrayList<String>();
}
Test code
#Test
public void test() {
testInheritance();
}
public void testInheritance() {
MyClass myClass = new MyClass("test", "someField");
myClass.myList.add("wow");
MyClassDao dao = new MyClassDao();
dao.save(myClass);
}
public class MyClassDao extends BasicDAO<MyClass, ObjectId> {
public MyClassDao() {
super(MyClass.class, MorphiaManager.getMongoClient(), MorphiaManager.getMorphia(), MorphiaManager.getDB().getName());
}
}
Result in DB
{
"_id" : "test",
"className" : "gr.iti.mklab.simmo.util.MyClass",
"someField" : "someField"
}
Interfaces can only declare method signatures and constants (static final variables). What you want to use is an abstract base class from which you inherit.
Additional observations from your code:
The id should be ob the type ObjectId and is automatically indexed, you don't need the #Indexed
Attributes should be private or protected and you need to provide getters and setters for them
You need a default no-arg constructor in your entity class