How to implement generic service and repository class in spring boot - java

I need to have generic service and repository classes in my project. I wrote it this way, but I don't know if it is correct or not.
BaseModel:
#MappedSuperclass
#Data
public class AbstractBaseEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Version
private int version;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public AbstractBaseEntity() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
}
GenericService:
public interface AbstractBaseService<T extends AbstractBaseEntity, ID extends Serializable>{
public abstract T save(T entity);
public abstract List<T> findAll(); // you might want a generic Collection if u prefer
public abstract Optional<T> findById(ID entityId);
public abstract T update(T entity);
public abstract T updateById(T entity, ID entityId);
public abstract void delete(T entity);
public abstract void deleteById(ID entityId);
// other methods u might need to be generic
}
Here I inherited from base service class and Autowired repository base classes.
#Service
#Transactional
public abstract class AbstractBaseServiceImpl<T extends AbstractBaseEntity, ID extends Serializable>
implements AbstractBaseService<T, ID> {
#Autowired
private AbstractBaseRepository<T, ID> abstractBaseRepository;
protected abstract Class<T> getDomaimClass();
protected Class<T> domainClass = this.getDomaimClass();
#Override
public T save(T entity) {
return (T) abstractBaseRepository.save(entity);
}
#Override
public List<T> findAll() {
return abstractBaseRepository.findAll();
}
#Override
public Optional<T> findById(ID entityId) {
return abstractBaseRepository.findById(entityId);
}
#Override
public T update(T entity) {
return (T) abstractBaseRepository.save(entity);
}
#Override
public T updateById(T entity, ID entityId) {
Optional<T> optional = abstractBaseRepository.findById(entityId);
if (optional.isPresent()) {
return (T) abstractBaseRepository.save(entity);
} else {
return null;
}
}
#Override
public void delete(T entity) {
abstractBaseRepository.delete(entity);
}
#Override
public void deleteById(ID entityId) {
abstractBaseRepository.deleteById(entityId);
}
GenericRepository:
I created a basic repository class and because I needed a custom Query that was not supported by jpa, I had to add two more classes.
#Repository
public interface AbstractBaseRepository<T extends AbstractBaseEntity, ID extends Serializable> extends JpaRepository<T, ID>,AbstractBaseRepositoryCustom {
}
public interface AbstractBaseRepositoryCustom<T extends AbstractBaseEntity,ID extends Serializable> {
List<T> findAllByFiter(String textQuery);
}
public class AbstractBaseRepositoryCustomImpl<T extends AbstractBaseEntity,ID extends Serializable> implements AbstractBaseRepositoryCustom {
#PersistenceContext
EntityManager entityManager;
#Override
public List findAllByFiter(String textQuery) {
Query query=entityManager.createQuery(textQuery);
return query.getResultList();
}
}
I don't know that the methods and wiring in my service class are correct. Is the repository installed correctly? Please help.

Related

Is it possible in Hibernate to override entity field name in a child entity?

I'm writing an app using Spring Boot, Hiberane and Spring Data.
I have two tables in the db: tableA and tableB.
They have some common fields but their id's,name's are different, also I've created a basic model for them to contain some common fields, right now it looks something like this:
// BaseModel
#MappedSuperclass
public abstract class BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="common_field_1")
private String commonField1;
#Column(name="common_field_2")
private String commonField2;
#Column(name="common_field_3")
private String commonField3;
}
// ExactModel 1
#Entity
#Table(name="table1" ,schema="schema")
public class ExactModel1 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_1", nullable=false)
private long exactModel1Id;
private String exactField1;
}
// ExactModel 2
#Entity
#Table(name="table2" ,schema="schema")
public class ExactModel2 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_2", nullable=false)
private long exactModel2Id;
private String exactField2;
}
And I have some generic logic which implements some general crud logic which works for classes which extend BaseModel:
public abstract class BaseServiceImpl<M extends BaseModel, R extends BaseRepository<M>> implements BaseService<M, Long> {
private final R repository;
public BaseServiceImpl(R repository) {
this.repository = repository;
}
#Override
public M save(M model) {
return repository.save(model);
}
#Override
public List<M> saveAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public M findById(Long id) {
return repository.getOne(id);
}
#Override
public List<M> findAllById(List<Long> ids) {
return repository.findAllById(ids);
}
#Override
public List<M> findAll() {
return repository.findAll();
}
#Override
public M update(M model) {
return repository.save(model);
}
#Override
public List<M> updateAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public void delete(M model) {
repository.delete(model);
}
#Override
public void delteById(Long id) {
repository.deleteById(id);
}
#Override
public void deleteInBatch(List<M> models) {
repository.deleteInBatch(models);
}
#Override
public Long countModels() {
return repository.count();
}
}
The thing is now I need to get somehow the id of the entity I work with in this generic logic, but there is no id field in BaseModel, so I can't just use baseModel.getId().
The question: is it possible to define a mock id field in BaseModel and override it in the child classes, so I can use this id in the generic methods but Hibernate fills the actual ids on the runtime for me?

Single repository for multiple objects with #DiscriminatorValue

I have several entities sharing the same database table with #DiscriminatorValue with one base class:
#Entity(name = "base_details")
#DiscriminatorColumn(name = "type")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Getter
#Setter
public abstract class BaseDetails {
#Id
#Column(name = "identifier")
private String identifier;
#Column(name = "item_type")
#Enumerated(EnumType.STRING)
private Type type;
}
#Entity
#DiscriminatorValue("ONE")
#Getter
#Setter
public class FirstDetails extends BaseDetails {
...
}
#Entity
#DiscriminatorValue("TWO")
#Getter
#Setter
public class SecondDetails extends BaseDetails {
...
}
This works fine but I am running into an issue with having too many separate repositories:
#Repository
public interface OneDetailsRepository extends JpaRepository<OneDetails, String> { ... }
etc.
Is there a way how to have a single repository (eq. BaseDetailsRepository) which would work with all derived entities?
EDIT:
What I am looking for is something like this:
#Repository
public interface BaseDetailsRepository extends JpaRepository<? extends BaseDetails, String> {
OneDetails findOne(String identifier);
SecondDetails findOne(String identifier);
}
You can do as follows :
Repository : BaseDetailsRepo.java
#Repository
public interface BaseDetailsRepo extends JpaRepository<BaseDetails, String>{
//make the return according to you ..it shouldn't be list....
#Query("FROM SecondDetails AS bd WHERE bd.identifier=:identifier")
public List<SecondDetails> getSecondDetailsByIdentifier(#Param("identifier") String identifier);
#Query("FROM FirstDetails AS bd WHERE bd.identifier=:identifier")
public List<FirstDetails> getFirstDetailsByIdentifier(#Param("identifier") String identifier);
}
Controller : DemoController.java
#Controller
public class DemoController {
#Autowired
private BaseDetailsRepo baseDetailsRepo;
#GetMapping(value="/test")
public ResponseEntity test(){
FirstDetails fd = new FirstDetails();
fd.setIdentifier("fd1-demo");
FirstDetails fd1 = new FirstDetails();
fd1.setIdentifier("fd2-demo");
SecondDetails sd = new SecondDetails();
sd.setIdentifier("sd1-demo");
baseDetailsRepo.save(fd);
baseDetailsRepo.save(fd1);
baseDetailsRepo.save(sd);
return new ResponseEntity("Success",HttpStatus.OK);
}
#GetMapping(value="/test/second/{id}")
public ResponseEntity getSecondDetails(#PathVariable String id){
return new ResponseEntity(baseDetailsRepo.getSecondDetailsByIdentifier(id),HttpStatus.OK);
}
#GetMapping(value="/test/first/{id}")
public ResponseEntity getFirstDetails(#PathVariable String id){
return new ResponseEntity(baseDetailsRepo.getFirstDetailsByIdentifier(id),HttpStatus.OK);
}
}
Output
#NoRepositoryBean // Read only repository
public interface BaseDetailsRepository<T extends BaseDetails>
extends JpaRepository<T, String> {
T findOne(String identifier);
T findOne(Long id);
Iterable<T> findAll();
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
and you can have also three repositories for each entity:
#Transactional
public interface BaseRepository extends BaseDetailsRepository<BaseDetails> { }
#Transactional
public interface FirstDetailsRepository extends BaseDetailsRepository<BaseDetails>,JpaRepository<T extends BaseDetails, String>{ }
#Transactional
public interface SecondDetailsRepository extends BaseDetailsRepository<BaseDetails>,JpaRepository<T extends BaseDetails, String> { }

Spring CrudRepository not working save & delete operation

In Spring, CrudRepository findAll() operation working good for fetching data from the database but with the same configuration in case of saving, update & delete it's not working.
EmployeeService.java
#Service
public class EmployeeService {
#Autowired
private EmployeeRepo employeeRepoI;
#Transactional
public List<Employee> getAllEmployee() {
return (List<Employee>) employeeRepoI.findAll();
}
#Transactional
public Employee getEmployee(int id) {
return (Employee) employeeRepoI.findOne(id);
}
#Transactional
public Employee addEmployee(Employee employee) {
return (Employee) employeeRepoI.save(employee);
}
#Transactional
public Employee updateEmployee(Employee employee) {
return (Employee) employeeRepoI.save(employee);
}
#Transactional
public void deleteEmployee(int id) {
employeeRepoI.delete(id);
}
}
EmployeeRapo.java
#Repository
public interface EmployeeRepo<T, ID extends Serializable> extends CrudRepository<Employee, Long> {
List<Employee> findAll();
}
As pointed out by #Sergey Your EmployeeRepo has a wrong definition there
Try this
#Repository
public interface EmployeeRepo extends CrudRepository<Employee, Long> {
List<Employee> findAll();
}
Also your deleteEmployee() method takes an int while it should take Long as a parameter.
#Transactional
public void deleteEmployee(Long id) {
employeeRepoI.delete(id);
}
You have CrudRepository with Long type and deleteEmployee with primitive int. This values should match.

How to implement generic business logic methods from JPA database entity class?

I want suggestion regarding a scenario I've been thinking of doing if possible. Suppose I have some JPA database entity class like:
#Entity
public class Person {
#Column(name = "ID")
private Long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
public String getFirstName(){
return this.firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public String getLastName(){
return this.lastName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
}
I am using EJB services. I can use separate business logic methods to make CRUD operation over these entities. Is it possible to use a generic template CRUD operations for these entity classes? Like if I want to create new person I will provide the Person entity class and fields to set as parameter and my generic method will create a new Person record and will do the same job for Read, Update and Delete operation as well.
Any respective example will be highly appreciated.
Thank You
Using EJB and JPA
You can consider an abstract class for the service layer:
public abstract class AbstractFacade<E extends Serializable,
PK extends Serializable> {
private final transient Class<E> entityClass;
public AbstractFacade(final Class<E> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(final E entity) {
final EntityManager entityManager = getEntityManager();
entityManager.persist(entity);
}
public final E find(final PK id) {
return getEntityManager().find(entityClass, id);
}
// Other common operations
}
And a particular service:
#Stateless
public class PersonFacade extends AbstractFacade<Person, Long> {
#PersistenceContext(unitName = "MyPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public PersonFacade() {
super(Person.class);
}
// Other methods of this service
}
Using Spring and Hibernate
You could have a abstract base class for common DAO methods.
public abstract class AbstractDAO<E extends Serializable,
PK extends Serializable> {
private final transient Class<E> entityClass;
public AbstractDAO(final Class<E> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public final E find(final PK id) {
return getEntityManager().find(entityClass, id);
}
// Another common methods
}
In every DAO implementation, you can put particular methods for that DAO.
#Repository
public final class PersonDAO extends AbstractDAO<Person, Long> {
#Autowired
private transient EntityManagerFactory emf;
public PersonDAO() {
super(Person.class);
}
#Override
protected EntityManager getEntityManager() {
return emf.createEntityManager();
}
// particular methods for this DAO
}
What about if the user not exists? Put this logic in the service layer.
#Service
public final class PersonService {
private static final Logger LOG = LoggerFactory.getLogger(PersonService.class);
#Autowired
private transient PersonDAO personDAO;
public Person findPerson(final Long id) {
return personDAO.find(id);
}
}
If you are using Spring then use Spring Data which will do all this for you.
http://docs.spring.io/spring-data/jpa/docs/1.4.2.RELEASE/reference/html/repositories.html#repositories.core-concepts

Why GET (JAX-RS) doesn't return inherited entity specific information?

I am running a RESTful web service on Glassfish 3.1.2 server. I use Jersey as JAX-RS implementation, Jackson as it's JSON provider, and (JPA) EclipseLink 2.5 as persistence provider for MySQL.
I'll introduce some context and then ask my question.
Let's assume we have the following hierarchy: Item entity and CraftableItem entity that inherits it (see code samples below). They both do have corresponding EAO and Resource classes: ItemEAO, CraftableItemEAO + ItemResource, CraftableItemResource (see code samples below).
Let's have two records in items table:
id | name
1 | "craftable"
2 | "non craftable"
and one corresponding record for "craftable" in crafting_items_joined:
id | crafting_time
1 | 120000
I want to get as more specific information about each entity from GET /item request as possible. I want it to return [{"id":1,"name":"craftable", "crafting_time":120000}, {"id":2,"name":"non craftable"}]. At the momment, GET /item/1 returns {"id":1,"name":"craftable", "crafting_time":120000} and GET /item/2 returns just {"id":2,"name":"non craftable"} which is the behavior i want, but for GET /item. Getting all information about entity present.
Code samples (setters are omitted for sanity's sake).
#MappedSuperclass
public abstract class BaseEntity {
protected Long id;
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "items")
public class Item extends BaseEntity implements Serializable {
private String name;
#Column(name = "name", nullable = false, unique = true)
public String getName() {
return name;
}
}
#Entity
#DiscriminatorValue("craftable")
#Table(name = "craftable_items_joined")
public class CraftableItem extends Item {
protected long craftingTime;
#Column(name = "crafting_time", nullable = false)
public long getCraftingTime() {
return craftingTime;
}
}
EAO then:
public abstract class AbstractEAO<T> {
private Class<T> entityClass;
public AbstractEAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
public Class<T> getEntityClass() {
return entityClass;
}
protected abstract EntityManager getEntityManager();
// create, edit, remove
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
}
#Stateless
#LocalBean
public class ItemEAO extends AbstractEAO<Item> {
#PersistenceContext(unitName = "primary")
private EntityManager entityManager;
public ItemEAO() {
super(Item.class);
}
#Override
protected EntityManager getEntityManager() {
return entityManager;
}
}
#Stateless
#LocalBean
public class CraftableItemEAO extends AbstractEAO<CraftableItem> {
#PersistenceContext(unitName = "primary")
private EntityManager entityManager;
public CraftableItemEAO() {
super(CraftableItem.class);
}
#Override
protected EntityManager getEntityManager() {
return entityManager;
}
}
Resource finally:
public abstract class AbstractResource<T extends BaseEntity> {
protected abstract AbstractEAO<T> getEAO();
#GET
public List<T> findAll() {
return getEAO().findAll();
}
#GET
#Path("{id}")
public T find(#PathParam("id") Long id) {
return getEAO().find(id);
}
// create, edit, remove
}
#Stateless
#Path("item")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class ItemResource extends AbstractResource<Item> {
#EJB private ItemEAO itemEAO;
#Override
protected AbstractEAO<Item> getEAO() {
return itemEAO;
}
}
#Stateless
#Path("item/craftable")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class CraftableItemResource extends AbstractResource<CraftableItem> {
#EJB private CraftableItemEAO craftableItemEAO;
#Override
protected AbstractEAO<CraftableItem> getEAO() {
return craftableItemEAO;
}
}

Categories

Resources