Generic service for CRUD operations

In my Spring Boot project, I want to have a single service class for generic CRUD that can handle any entity, not to extend it, just to call it from controllers indicating entity, repository and DTO (Or something like this) the main idea is to have a single service class to CRUD to all entities, if the entity have unique or different behavior I will separate it in other service e.g. user authentication. Reference the entities, repositories and DTOs outside the CRUD service. Doing this to not repeat the same logic and creating a class for each entity.
7 Replies
JavaBot
JavaBot3w ago
This post has been reserved for your question.
Hey @Franscis123$#! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
Franscis123$#
Franscis123$#OP3w ago
This service template dedicated to Flavor entity I want can be used for all entities:
package com.latteIceCream.latte.service;

import com.latteIceCream.latte.domain.Flavor;
import com.latteIceCream.latte.dto.FlavorDTO;
import com.latteIceCream.latte.repository.FlavorRepository;

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class FlavorService

{

@Autowired
FlavorRepository flvRepo;

@Transactional
public Flavor create(FlavorDTO flvDTO)

{ return flvRepo.save(new Flavor(flvDTO.name(), flvDTO.description(), flvDTO.available())); }

public Flavor read(Long id) { return flvRepo.findById(id).orElse(null); }

@Transactional
public Flavor update(Long id, FlavorDTO flvDTO)

{

return flvRepo.findById(id).map(flavor ->

{

if (flvDTO.name() != null) { flavor.setName(flvDTO.name()); }
if (flvDTO.description() != null) { flavor.setDescription(flvDTO.description()); }
if (flvDTO.available() != null) { flavor.setAvailable(flvDTO.available()); }

/*
Could be more convenient update the entity entirely instead attribute-by-attribute, there is x number of attributes by entity.
*/

return flvRepo.save(flavor);

}

).orElse(null);

}

@Transactional
public boolean delete(Long id)

{

if(flvRepo.findById(id).isEmpty()) { return false; }

flvRepo.deleteById(id); return true;

}

public List<Flavor> getAll() { return flvRepo.findAll(); }

}
package com.latteIceCream.latte.service;

import com.latteIceCream.latte.domain.Flavor;
import com.latteIceCream.latte.dto.FlavorDTO;
import com.latteIceCream.latte.repository.FlavorRepository;

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class FlavorService

{

@Autowired
FlavorRepository flvRepo;

@Transactional
public Flavor create(FlavorDTO flvDTO)

{ return flvRepo.save(new Flavor(flvDTO.name(), flvDTO.description(), flvDTO.available())); }

public Flavor read(Long id) { return flvRepo.findById(id).orElse(null); }

@Transactional
public Flavor update(Long id, FlavorDTO flvDTO)

{

return flvRepo.findById(id).map(flavor ->

{

if (flvDTO.name() != null) { flavor.setName(flvDTO.name()); }
if (flvDTO.description() != null) { flavor.setDescription(flvDTO.description()); }
if (flvDTO.available() != null) { flavor.setAvailable(flvDTO.available()); }

/*
Could be more convenient update the entity entirely instead attribute-by-attribute, there is x number of attributes by entity.
*/

return flvRepo.save(flavor);

}

).orElse(null);

}

@Transactional
public boolean delete(Long id)

{

if(flvRepo.findById(id).isEmpty()) { return false; }

flvRepo.deleteById(id); return true;

}

public List<Flavor> getAll() { return flvRepo.findAll(); }

}
ayylmao123xdd
ayylmao123xdd3w ago
isnt an interface with methods like create etc better and then you can just implement it
Franscis123$#
Franscis123$#OP3w ago
I do it, that is the simple way, but the idea is not repeat that logic creating a class for each entity
JavaBot
JavaBot3w ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
Franscis123$#
Franscis123$#OP3w ago
I got it this way:
package com.latteIceCream.latte.service;

import com.latteIceCream.latte.dto.DTO;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public class CrudService<T>

{

JpaRepository<T, Long> repo;

public CrudService(JpaRepository<T, Long> repository) { this.repo = repository; }

@Transactional
public T create(DTO<T> dto) { return repo.save(dto.toEntity()); }

public T read(Long id) { return repo.findById(id).orElse(null); }

@Transactional
public T update(Long id, DTO<T> dto)

{

return repo.findById(id).map(entity -> {

dto.setFields(entity);
return repo.save(entity);

}).orElse(null);

}

@Transactional
public boolean delete(Long id)

{

if(repo.findById(id).isEmpty()) { return false; }

repo.deleteById(id); return true;

}

public List<T> getAll() { return repo.findAll(); }

}
package com.latteIceCream.latte.service;

import com.latteIceCream.latte.dto.DTO;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public class CrudService<T>

{

JpaRepository<T, Long> repo;

public CrudService(JpaRepository<T, Long> repository) { this.repo = repository; }

@Transactional
public T create(DTO<T> dto) { return repo.save(dto.toEntity()); }

public T read(Long id) { return repo.findById(id).orElse(null); }

@Transactional
public T update(Long id, DTO<T> dto)

{

return repo.findById(id).map(entity -> {

dto.setFields(entity);
return repo.save(entity);

}).orElse(null);

}

@Transactional
public boolean delete(Long id)

{

if(repo.findById(id).isEmpty()) { return false; }

repo.deleteById(id); return true;

}

public List<T> getAll() { return repo.findAll(); }

}
package com.latteIceCream.latte.config;

import com.latteIceCream.latte.repository.FlavorRepository;
import com.latteIceCream.latte.domain.Flavor;
import com.latteIceCream.latte.service.CrudService;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

@Configuration
public class ServiceConfig

{

@Bean
public CrudService<Flavor> flavorService(FlavorRepository flavorRepository)

{ return new CrudService<>(flavorRepository); }

// I can add more of the same way

}
package com.latteIceCream.latte.config;

import com.latteIceCream.latte.repository.FlavorRepository;
import com.latteIceCream.latte.domain.Flavor;
import com.latteIceCream.latte.service.CrudService;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

@Configuration
public class ServiceConfig

{

@Bean
public CrudService<Flavor> flavorService(FlavorRepository flavorRepository)

{ return new CrudService<>(flavorRepository); }

// I can add more of the same way

}
In the controller:
@Autowired
CrudService<Flavor> crudService;
@Autowired
CrudService<Flavor> crudService;
JavaBot
JavaBot3w ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.

Did you find this page helpful?