How can I refactor this method? (spring + java8) - spring

I am trying to refactor the Java class below. I have a method that saves a POJO (entity) depending on which instance it belongs to.
The code below shows only 3 services but there is 13 service in total.
Each service is calling a separate *RepositoryImpl.
For instance, the ActiviteService is an interface and the activiteService.create(activity) will call the implementation of that interface.
#Autowired
private ActiviteService activiteService;
#Autowired
private AdresseMsSanteService adresseMsSanteService;
#Autowired
private AttributionParticuliereService attributionParticuliereService;
private boolean sauvegarder(Object object, Long idIdPlay, String game,
Integer gameIndex) {
boolean isSaved = false;
if (idIdPlay == null) {
throw new IllegalArgumentException("IdPlay id is null");
}
if (object instanceof Activite) {
Activite activite = (Activite) object;
activite.setIdIdPlay(idIdPlay);
if (this.isGameOn(activite, game, gameIndex)) {
activiteService.create(activite);
isSaved = true;
}
} else if (object instanceof AdresseMsSante) {
AdresseMsSante adresseMsSante = (AdresseMsSante) object;
adresseMsSante.setIdIdPlay(idIdPlay);
if (this.isGameOn(adresseMsSante, game, gameIndex)) {
adresseMsSanteService.create(adresseMsSante);
isSaved = true;
}
} else if (object instanceof AttributionParticuliere) {
AttributionParticuliere attributionParticuliere = (AttributionParticuliere) object;
attributionParticuliere.setIdIdPlay(idIdPlay);
if (this.isGameOn(attributionParticuliere, game, gameIndex)) {
attributionParticuliereService.create(attributionParticuliere);
isSaved = true;
}
} else if

Firstly, I would create an interface representing your Game Entity. For instance:
public interface GameEntity {
void setIdIdPlay(Long idIdPlay);
}
After that, you create the classes that implement the GameEntity interface:
#Entity
#Table
public class AdresseMsSante implements GameEntity {
#Id
Long idIdPlay;
public void setIdIdPlay(Long idIdPlay) {
this.idIdPlay = idIdPlay;
}
}
#Entity
#Table
public class Activite implements GameEntity {
#Id
Long idIdPlay;
public void setIdIdPlay(Long idIdPlay) {
this.idIdPlay = idIdPlay;
}
}
Then, create your generic repository which will save every Game Entity.
#Repository
public class Repo {
#Autowired
EntityManager entityManager;
#Transactional
public void save(GameEntity obj) {
entityManager.merge(obj);
}
}
Finally, your method will be like that:
#Autowired
Repo repo;
private boolean sauvegarder(Object object, Long idIdPlay, String game,
Integer gameIndex) {
boolean isSaved = false;
if (idIdPlay == null) {
throw new IllegalArgumentException("IdPlay id is null");
}
GameEntity gameEntity = (GameEntity) object;
gameEntity.setIdIdPlay(idIdPlay);
if(this.isGameOn(gameEntity, game, gameIndex)) {
repo.save(gameEntity);
isSaved = true;
}
return isSaved;
}
boolean isGameOn(GameEntity gameEntity, String game, Integer gameIndex) {
// do something
return true;
}

Related

How to register hibernate custom multiple EventListeners

My scenario is need yo track entity property changes. I have used Hibernate PostUpdateEventListener interface to achieve that.
Following is my generic event listener class.
public abstract class EventListener<DOMAIN extends BaseModel> implements PostUpdateEventListener {
public abstract LogSupport getService();
public abstract BaseModel getLogDomain(DOMAIN domain);
#SuppressWarnings("unchecked")
private DOMAIN getDomain(BaseModel model) {
return (DOMAIN) model;
}
public void postUpdate(PostUpdateEvent event, BaseModel model) {
getService().createUpdateLog(getDomain(model), getPostUpdateEventNotes(event));
}
private String getPostUpdateEventNotes(PostUpdateEvent event) {
StringBuilder sb = new StringBuilder();
for (int p : event.getDirtyProperties()) {
sb.append("\t");
sb.append(event.getPersister().getEntityMetamodel().getProperties()[p].getName());
sb.append(" (Old value: ")
.append(event.getOldState()[p])
.append(", New value: ")
.append(event.getState()[p])
.append(")\n");
}
System.out.println(sb.toString());
return sb.toString();
}
}
And this is my custom entity listener.
#Component
public class AssetEventListener extends EventListener<Asset> {
private static final long serialVersionUID = -6076678526514705909L;
#Autowired
#Qualifier("assetLogService")
private LogSupport logSupport;
#Override
public LogSupport getService() {
AutowireHelper.autowire(this, logSupport);
return logSupport;
}
#PostPersist
public void onPostInsert(PostInsertEvent event) {
if (event.getEntity() instanceof BaseModel){
super.postPersist( event, (BaseModel) event.getEntity() );
}
}
#Override
public void onPostUpdate(PostUpdateEvent event) {
if (event.getEntity() instanceof BaseModel){
super.postUpdate( event, (BaseModel) event.getEntity() );
}
}
#Override
public BaseModel getLogDomain(Asset domain) {
return domain;
}
#Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
}
And I called it from #EntityListeners
#Entity
#Table(name = "tbl_asset")
#EntityListeners({ AssetEventListener.class })
public class Asset extends BaseModel {
}
Listener not call when update the entity. Any help would be greatly appreciated.
Thanks,

#Embedded Audit was working with spring boot 1.4 but now not working after upgrade to spring boot 2.1.x

public interface EAuditable {
public static interface Update{
public EUpdateInfo getUpdateInfo();
public void setUpdateInfo(EUpdateInfo updateInfo);
}
public static interface Create{
public ECreateInfo getCreateInfo();
public void setCreateInfo(ECreateInfo createInfo);
}
public static interface UpdateDate{
public ZonedDateTime getLastUpdatedOn();
public void setLastUpdatedOn(ZonedDateTime date);
}
public static interface UpdateUser{
public String getLastUpdatedBy();
public void setLastUpdatedBy(String user);
}
public static interface CreateDate{
public ZonedDateTime getCreatedOn();
public void setCreatedOn(ZonedDateTime date);
}
public static interface CreateUser{
public String getCreatedBy();
public void setCreatedBy(String user);
}
}
#Entity
#Table(name = "TRACKABLE_ITEM")
#Inheritance(strategy = InheritanceType.JOINED)
public class ETrackableItem extends AbstractIdTenantPersistable implements EAuditable.Create, EAuditable.Update {
#Embedded
private ECreateInfo createInfo;
#Embedded
private EUpdateInfo updateInfo;
}
#MappedSuperclass
//JPA: For Eclipse Link, use #AdditionaliCritera instead of #Filter and #FilterDef
#FilterDef(name="multiTenant", parameters=#ParamDef( name="tenant_id", type="string" ) )
#Filter(name="multiTenant", condition=":tenant_id = TENANT_ID")
public abstract class AbstractIdTenantPersistable extends AbstractIdPersistable {
#Column(name = "TENANT_ID", length = 12)
protected String tenantId;
public String getTenantId() {
return tenantId;
}
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
protected AbstractIdTenantPersistable(){}
protected AbstractIdTenantPersistable(UUID id) {
super(id);
}
protected AbstractIdTenantPersistable(UUID id, String tenantId) {
super(id);
this.tenantId = tenantId;
}
}
#MappedSuperclass
#EntityListeners(value = { PersistableEntityListener.class })
#TypeDef(name = "json", typeClass = JsonUserType.class)
public abstract class AbstractPersistable {
public enum ConstraintType{UniqueName, Unique, Others};
/**
* Returns the constraint name to its type mapping.
* #param name
* #return
*/
public ConstraintType getConstraintType(String name){
return ConstraintType.Others;
}
}
public class PersistableEntityListener {
#PrePersist
public void prePersist(AbstractPersistable e) {
if (e instanceof EAuditable.Create){
EAuditable.Create create = (EAuditable.Create) e;
create.setCreateInfo(new ECreateInfo(ZonedDateTime.now(ZoneId.of("UTC")), getUserName()));
}
if (e instanceof EAuditable.CreateDate){
EAuditable.CreateDate createDate = (EAuditable.CreateDate) e;
createDate.setCreatedOn(ZonedDateTime.now(ZoneId.of("UTC")));
}
if (e instanceof EAuditable.CreateUser){
EAuditable.CreateUser createUser = (EAuditable.CreateUser) e;
createUser.setCreatedBy(getUserName());
}
if (e instanceof AbstractIdTenantPersistable){
((AbstractIdTenantPersistable)e).setTenantId(getTenantId());
}
preUpdate(e);
}
#PreUpdate
public void preUpdate(AbstractPersistable e) {
if (e instanceof EAuditable.Update){
EAuditable.Update update = (EAuditable.Update) e;
update.setUpdateInfo(new EUpdateInfo(ZonedDateTime.now(ZoneId.of("UTC")), getUserName()));
}
if (e instanceof EAuditable.UpdateDate){
EAuditable.UpdateDate updateDate = (EAuditable.UpdateDate) e;
updateDate.setLastUpdatedOn(ZonedDateTime.now(ZoneId.of("UTC")));
}
if (e instanceof EAuditable.UpdateUser){
EAuditable.UpdateUser updateUser = (EAuditable.UpdateUser) e;
updateUser.setLastUpdatedBy(getUserName());
}
}
private String getUserName() {
//checkContext();
if (isUserSet()){
return ContextHolder.get().getAuthenticatedContext().getUserName();
}
return "-";
}
private String getTenantId() {
//checkContext();
if (isTenantSet()){
return ContextHolder.get().getAuthenticatedContext().getTenantId();
}
return "-";
}
private boolean isUserSet(){
return ContextHolder.get() != null &&
ContextHolder.get().getAuthenticatedContext() != null &&
ContextHolder.get().getAuthenticatedContext().getUserName() != null;
}
private boolean isTenantSet(){
return ContextHolder.get() != null &&
ContextHolder.get().getAuthenticatedContext() != null &&
ContextHolder.get().getAuthenticatedContext().getTenantId() != null;
}
private boolean isContextSet(){
return isUserSet() && isTenantSet();
}
private void checkContext(){
BeanHolder.asserts().isTrue(
isContextSet(),`enter code here`
DomainException.class, "DOMAIN.CONTEXT_NOT_SET");
}
}
Below the code which was working fine for auto populate the audit info with the spring-data-jpa used with spring boot 1.4.x but it is not working when we upgraded to spring boot 2.1.x
Can any one please help if we have different way of handing for the same.
I need to follow this kind of #Embedded way of doing as here we are doing it by composition except inheritance.
Thanks in advance.

Read data from an Android Room database in a background Service, no exceptions but no data

I am attempting to read data from an Android Room database in a background Service. There are no exceptions but no data is returned.
I wrote a function to select all rows from a table in the DAO. Calling that function from a background service succeeds, but it returns no data.
My "Contact" class holds contact information (names, phone numbers, emails) and defines the database schema. The database holds rows of contacts, with names, phone numbers, an emails as columns.
The function that returns the LiveData in the DAO is:
#Query("SELECT * FROM contacts_table")
LiveData<List<Contact>> getAll();
where "contacts_table" is the database table holding contact information.
I called getAll as follows:
AppDatabase db = AppDatabase.getDatabase(messageSenderContext.getApplicationContext());
mContactDAO = db.contactDAO();
mAllContacts = mContactDAO.getAll();
where mContactDao is a ContactDAO (The Database Access Object for my Contact class), and mAllContacts is a LiveData>. These are private fields of the class calling getAll().
db.contactDAO() returns an object, as does mContactDAO.getAll(). But attempting to unpack the List from mAllContacts using mAllContacts.getValue() returns null.
This turned out to be a misuse of LiveData. That requires an Observer to actually get the data.
In your ROOM
#Database(entities={Contact.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public static final String DATABASE_NAME = "AppDatabase.db";
private static volatile AppDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
public abstract ContactDAO contactDAO();
public static AppDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, DATABASE_NAME)
.build();
}
}
}
return INSTANCE;
}
}
In your DAO
#Dao
public interface ContactDAO{
#Query("SELECT * FROM contacts_table")
LiveData<List<Contact>> getAll();
}
In your repository:
public class AppRepository {
private ContactDAO mContactDAO;
//constructor
public AppRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mContactDAO= db.contactDAO();
}
public LiveData<List<Contact>> getAllContacts(){
LiveData<List<Contact>> contactsList = null;
Future<LiveData<List<Contact>>> futureList = AppDatabase.EXECUTOR_SERVICE.submit(new Callable(){
#Override
public LiveData<List<Contact>> call() {
return contactDAO.getAll();
}
});
try {
contactsList = futureList.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return contactsList ;
}
}
In your ViewModel
public class ContactsViewModel extends AndroidViewModel {
private AppRepository appRepository;
private LiveData<List<Contact>> contactsList;
public ContactsViewModel(#NonNull Application application) {
super(application);
appRepository = new AppRepository(application);
}
public LiveData<List<Contacts>> list() {
return appRepository.getAllContacts();
}
}
In your activity (inside of onCreated put)
ContactsViewModel contactsViewModel = new ViewModelProvider(this).get(ContactsViewModel.class);
contactsViewModel.list().observe(getViewLifecycleOwner(), new Observer<List<Contact>>() {
#Override
public void onChanged(List<Contact> contactsList) {
//the contact list will be observed and will return data if there are changes.
//use for example to feed the adapter of a recyclerview
//below an example just to view the contacts data
for(Contact conctact : contactsList){
Log.d("TestApp>>>", "Id: + contact.getId);
Log.d("TestApp>>>", "Name: + contact.getName);
}
});

How does Spring's JPARepository and #Transactional behave together?

I have two methods (in a Spring boot application) that handle an entity. The entity has two fields, both boolean isDefault and isPdfGenerated. The first method (which is called from a controller) changes the isDefault flag when a new entity is created while the second one (called from a #Scheduled annotated method) changes the isPdfGenrated after it generates a pdf file for that entity.
My problem is that sometimes the second method finds entities with the isPdfGenerated flag set to false even though the file has been generated and saved in the database.
Both the methods have the #Transactional annotation and the repository interface for the entity extends JpARepository.
My guess is that the first method loads the entity from the database before the second method does but saves the entity after the second method does its job, thus overriding the isPdfGenerated flag.
Is this possible ? If the answer is yes, how should one handle such cases ? Shouldn't JPARepository handle the case when an entity gets updated from an external source ?
Bellow is some code to better illustrate the situation.
MyController:
#Controller
#RequestMapping("/customers")
public class MyController {
#Autowired
private EntityService entityService;
#RequestMapping(value = "/{id}/changeDefault", method = RequestMethod.POST)
public String changeDefault(#PathVariable("id") Long customerId, #ModelAttribute EntityForm entityForm, Model model) {
Entity newDefaultEntity = entityService.updateDefaultEntity(customerId, entityForm);
if (newDefaultEntity == null)
return "redirect:/customers/" + customerId;
return "redirect:/customers/" + customerId + "/entity/default;
}
}
EntityService:
import org.springframework.transaction.annotation.Transactional;
#Service
public class EntityService {
#Autowired
private EntityRepository entityRepository;
#Autowired
private CustomerRepository customerRepository;
#Transactional
public Entity updateDefaultEntity(Long customerId, submittedData) {
Customer customer = customerRepository.findById(customerId);
if(customer == null)
return customer; // I know there are better ways to do this
Entity currentDefaultEntity = entityRepository.findUniqueByCustomerAndDefaultFlag(customer, true);
if(currentDefaultEntity == null)
return null; // I know there are better ways to do this also
Entity newDefaultEntity = new Entity();
newDefaultEntity.setField1(submittedData.getField1());
newDefaultEntity.setField2(submittedData.getField2());
newDefaultEntity.setCustomer(customer);
oldDefaultEntity.setDefaultFlag(false);
newDefaultEntity.setDefaultFlag(true);
entityRepository.save(newDefaultEntity);
}
#Transactional
public void generatePdfDocument(Entity entity) {
Document pdfDocument = generateDocument(entity);
if(pdfDocument == null)
return;
documentRepository.save(pdfDocument);
entity.setPdfGeneratedFlag(true);
entityRepository.save(entity);
}
}
ScheduledTasks:
#Component
public class ScheduledTasks {
private static final int SECOND_IN_MILLISECONDS = 1000;
private static final int MINUTE_IN_SECONDS = 60;
#Autowired
private EntityRepository entityRepository;
#Autowired
private DocumentService documentService;
#Scheduled(fixedDelay = 20 * SECOND_IN_MILLISECONDS)
#Transactional
public void generateDocuments() {
List<Quotation> quotationList = entityRepository.findByPdfGeneratedFlag(false);
for(Entity entity : entitiesList) {
documentService.generatePdfDocument(entity);
}
}
}
DocumentService:
#Service
public class DocumentService {
#Autowired
private EntityRepository entityRepository;
#Autowired
private DocumentRepository documentRepository;
#Transactional
public void generatePdfDocument(Entity entity) {
Document pdfDocument = generateDocument(entity);
if(pdfDocument == null)
return;
documentRepository.save(pdfDocument);
entity.setPdfGeneratedFlag(true);
entityRepository.save(entity);
}
}
EntityRepository:
#Repository
public interface EntityRepository extends JpaRepository<Entity, Long> {
Entity findById(#Param("id") Long id);
List<Entity> findByPdfGeneratedFlag(#Param("is_pdf_generated") Boolean pdfGeneratedFlag);
Entity findUniqueByCustomerAndDefaultFlag(
#Param("customer") Customer customer,
#Param("defaultFlag") Boolean defaultFlag
);
}
DocumentRepository:
#Repository
public interface DocumentRepository extends JpaRepository<Document, Long> {
Document findById(#Param("id") Long id);
}
Entity:
#Entity
#Table(name = "entities")
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
public class Entity {
private Long id;
private boolean defaultFlag;
private boolean pdfGeneratedFlag;
private String field1;
private String field2;
private Customer customer;
public Entity() { }
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "is_default")
public boolean isDefaultFlag() {
return defaultFlag;
}
public void setDefaultFlag(boolean defaultFlag) {
this.defaultFlag = defaultFlag;
}
#Column(name = "is_pdf_generated")
public boolean isPdfGeneratedFlag() {
return pdfGeneratedFlag;
}
public void setPdfGeneratedFlag(boolean pdfGeneratedFlag) {
this.pdfGeneratedFlag = pdfGeneratedFlag;
}
#Column(name = "field_1")
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
#Column(name = "field_2")
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
#ManyToOne
#JoinColumn(name = "customer_id", referencedColumnName = "id", nullable = false)
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entity quotation = (Entity) o;
return id != null ? id.equals(entity.id) : entity.id == null;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
#Override
public String toString() {
return "Entity{" +
"id=" + id +
", pdfGeneratedFlag=" + pdfGeneratedFlag +
", defaultFlag=" + defaultFlag +
", field1=" + field1 +
", field2=" + field2 +
", customer=" + (customer == null ? null : customer.getId()) +
"}";
}
}
I have omitted the other classes because they are either POJOs ( EntityForm ) or the same as other domain model classes ( Document ).
If you're talking about a row on the database that is getting updated by another process after the first process has read it but before it has been updated, then you need to put in some sort of optimistic locking strategy.
This will be handled by the underlying ORM api (e.g. Hibernate or Eclipselink) rather than Spring Data (which will just handle an optimistic locking errors thrown by the ORM).
Have a look at this article. Bear in mind that if you want optimistic locking you need some way of determining a row's version. In JPA this is normally done using a column annotated with the #Version tag.
https://vladmihalcea.com/hibernate-locking-patterns-how-does-optimistic-lock-mode-work/

Generate static map from database using a singleton class also using spring configuration #Autowired

I need to create an unmodifiable map generated from data obtained by querying a database. How, or can I, or is there a better way to do this using spring annotations?
I ran into a problem when creating a singleton for my Regions class and then trying to #Autowire in a RegionService to grab the object from the DAO. The problem is that spring can't instantiate the RegionService because it needs to instantiate the static singleton class Regions which needs to get data from the database as shown below in the constructor.
Please see me classes below (I've removed multiple unneeded methods that don't pertain to this question):
public final class Region {
private static final String DEFAULT_SEPERATOR = "-";
private final Integer key;
private final String description;
public Region(Integer pKey, String pDescription) {
this.key = pKey;
this.description = pDescription;
}
public Integer getKey() {
return this.key;
}
public String getValue() {
return this.description;
}
}
Here is my singleton:
public final class Regions {
private static Regions regionsInstance = null;
#Autowired
private RegionService regionService;
static Map<Integer, Region> regions;
private Regions() {
final Map<Integer, Region> tempRegions = new HashMap<Integer, Region>();
for (final Region region : this.regionService.retrieveAll()) {
tempRegions.put(region.getKey(), region);
}
regions = Collections.unmodifiableMap(tempRegions);
}
public static synchronized Regions getRegionsInstance() {
if (regionsInstance == null) {
regionsInstance = new Regions();
}
return regionsInstance;
}
public Region getRegion(final Integer pKey) {
return regions.get(pKey);
}
public List<Region> getRegions() {
return (List<Region>) regions.values();
}
}
My DAO and Service are just interfaces, no need to post those, here are my Impls:
#Service
public class RegionServiceImpl implements RegionService {
#Autowired
private RegionDAO regionDao;
#Override
public List<Region> retrieveAll() {
return this.regionDao.retrieveAll();
}
}
My DAOImpl (tested and works, just posting to give you the full picture):
#Repository
public class RegionDAOImpl implements RegionDAO {
private static final String SQL_RETRIEVE_REGIONS = "some random SQL";
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public List<Region> retrieveAll() {
try {
return this.jdbcTemplate.query(SQL_RETRIEVE_REGIONS, new ResultSetExtractor<List<Region>>() {
#Override
public List<Region> extractData(ResultSet rs) throws SQLException, DataAccessException {
return RegionDAOImpl.this.mapRegionData(rs);
}
});
} catch (final DataAccessException dae) {
throw new DaoException("Could not retrieve regionList from database. " + dae);
}
}
protected final List<Region> mapRegionData(ResultSet rs) throws SQLException {
final List<Region> regionList = new ArrayList<Region>();
while (rs.next()) {
regionList.add(new Region(rs.getInt("REGION_CD"), rs.getString("REGION_TXT")));
}
return Collections.unmodifiableList(regionList);
}
}
Then I run my test(I took out unneeded crap):
#..annotated with things you don't need to know
public class RetrieveRegionsTest {
#Autowired
private Regions r;
#Test
public void getAndLogRegion() {
final List<Region> regionDescriptions = new ArrayList<Region>(this.r.getRegions());
for (final Region region : regionDescriptions) {
LOGGER.info(region.getValue());
}
}
Yes my configuration and classpaths are set up properly. I can get this to work other ways, just not by accessing the Regions singleton which is what I want. Now I know I could take off the #Autowired on the RegionService in my Regions singleton and just create a new instance of RegionService, but that would defeat the purpose of springs #Autowired feature.
Any thoughts, ideas, comments?

Resources