Convert Long id to Set list using Mapstruct? - spring-boot

In a Mapstruct interface, how can I convert an id to a Set< UserSystem > ?
I tried as follows but unsuccessfully because error occurs:
#Mapper(componentModel = "spring", uses = {UserSystemService.class})
public interface CompanyPostMapper extends EntityMapper<CompanyPostDTO, Company> {
#Mapping(source = "userSystemId", target = "userSystems", expression = "java(userSystemService.findByIdAndAddToSet(id))")
Company toEntity(CompanyPostDTO dto);
default Company fromId(Long id) {
if (id == null) {
return null;
}
Company company = new Company();
company.setId(id);
return company;
}
}
I don't know if I understood the use of the "uses" parameter correctly, but basically I would like to get the ID and query the register and return a Set with the register.
I was going to try "qualifiedByName" and create a method in the Mapper interface, but I don't know how I can inject the Repository and I don't know if that would be a good practice.
What would be the best way to solve?

MapStruct is a mapping framework. You are doing a lookup inside the mapping. It is possible of course (look at the JPA mapping example where an #Context is used). But you can't inherit EntityMapper<CompanyPostDTO, Company> at the same time.
Normally, you need to take the lookup outside your mapping logic and use an update method to update the object.
Your mapping would then look like:
#Mapper(componentModel = "spring" )
public interface CompanyPostMapper {
void updateEntity(CompanyPostDTO dto, #MappingTarget Company entity);
}
// and your call would look like:
public class CallingService{
Company company = userSystemService.findByIdAndAddToSet(id));
if (company == null) {
company = new Company();
}
companyPostMapper.updateEntity(dto, company);
}

Related

Two-way binding in Android with data from Room database

I am new to the MVVM architecture in Android, and I have some days with a doubt that I consider basic, but that I can't solve.
I proceed to discuss my problem:
I have an Entity, CustomerView (this entity is created from a DatabaseView):
#DatabaseView("select ... ")
public class CustomerView {
public String cardCode;
public String cardName;
public String cardFName;
...
Then, I have a Dao class:
#Dao
public interface OCRD_DAO {
...
#Query("SELECT * from CustomerView where cardCode= :cardCode")
LiveData<CustomerView> getCustomerViewByCardCode(String cardCode);
...
}
The repository class, makes use of the DAO class:
public LiveData<CustomerView> getCustomer(String cardCode){
return mOcrdDao.getCustomerViewByCardCode(cardCode);
}
The CustomerSheetViewModel class:
public class CustomerSheetViewModel extends BaseObservable {
private Repository mRepository;
public LiveData<CustomerView> mCustomer;
private MutableLiveData<String> _cardName;
#Bindable
public MutableLiveData<String> getCardName(){
return this._cardName;
}
public MutableLiveData<String> setCardName(String value){
// Avoids infinite loops.
if (mCustomer.getValue().cardName != value) {
mCustomer.getValue().cardName = value;
// React to the change.
saveData();
// Notify observers of a new value.
notifyPropertyChanged(BR._cardName);
}
}
public CustomerSheetViewModel (Application application, String cardCode) {
mRepository = new Repository(application);
this.mCustomer = mRepository.getCustomer(cardCode);
//Init MutableLiveData????
this._cardName = this.mCustomer.getValue().cardName;
//Null Exception, because this.mCustomer.getValue() is null
}
}
At this point, my problem occurs: when I initialise the CustomerView object, it is of type LiveData. However, if I want to make use of 2-way binding, I need an object of type MutableLiveData. So, I think I should create the MutableLiveData object with the data extracted from the database (i.e. from the call to the repository). When I try this (e.g. getValue().cardName) a null exception is thrown, since LiveData is asynchronous.
Finally, I could make use of this property in the layout:
android:text="#={customerSheetViewModel.cardName}"
I really appreciate any help, as I can't find any reference to 2-way binding when the data comes from a database read.
Thanks in advance.

Repository vs. DAO (again)

In general this back-story does not matter but just to explain the code below:
The server handles users and user groups. User groups are able to "discover" places - at this point in time these places are coming exclusively from the Google Places API.
Current Implementation
Currently, I have a lot of JpaRepository objects, which I call Repository, in my Service Layer. I am stressing "Repository" because in my proposed solution below, they'd be downgraded to DAOs.
However, what I do not like in my current code, and also the reason for my question here, is the amount of repositories one can find in the UserGroupService.
#Service
public class UserGroupService {
private final static Logger LOGGER = LogManager.getLogger(UserGroupService.class);
#Autowired
private UserGroupRepository userGroupRepository;
#Autowired
private UserGroupPlaceRepository userGroupPlaceRepository;
#Autowired
private PlaceRepository placeRepository;
#Autowired
private GooglePlaceRepository googlePlaceRepository;
#Autowired
private GooglePlaces googlePlaces;
public UserGroupService() {
}
#Transactional
public void discoverPlaces(Long groupId) {
final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null);
if (userGroup == null) {
throw new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
}
List<PlacesSearchResult> allPlaces = this.googlePlaces.findPlaces(
userGroup.getLatitude(),
userGroup.getLongitude(),
userGroup.getSearchRadius());
allPlaces.forEach(googlePlaceResult -> {
GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);
if (googlePlace != null) {
return;
}
Place place = new Place();
place.setLatitude(googlePlaceResult.geometry.location.lat);
place.setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
place.setName(googlePlaceResult.name);
place.setVicinity(googlePlaceResult.vicinity);
place = this.placeRepository.save(place);
UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new UserGroupPlace.UserGroupPlaceId();
userGroupPlaceId.setUserGroup(userGroup);
userGroupPlaceId.setPlace(place);
UserGroupPlace userGroupPlace = new UserGroupPlace();
userGroupPlace.setUserGroupPlaceId(userGroupPlaceId);
this.userGroupPlaceRepository.save(userGroupPlace);
googlePlace = new GooglePlace();
googlePlace.setPlace(place);
googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
this.googlePlaceRepository.save(googlePlace);
});
}
}
A Solution That Does Not Work
What could make this code a lot simpler and had the potential to resolve this mess up there, would be #Inheritance:
#Entity
#Table(name = "place")
#Inheritance(strategy InheritanceType.JOINED)
public class Place { /* .. */ }
#Entity
#Table(name = "google_place")
public class GooglePlace extends Place { /* .. */ }
However, this is not an option because then I cannot have a PlaceRepository which saves just a place. Hibernate does not seem to like it..
My proposal
I think my confusion starts with the names that Spring is using. E.g. JpaRepository - I am not so sure if this is actually "the right" name. Because as far as I understood, these objects actually work like data access objects (DAOs). I think it should actually look something like this:
public interface PlaceDao extends JpaRepository<Place, Long> {
}
public interface GooglePlaceDao extends JpaRepository<Place, Long> {
}
#Repository
public class GooglePlaceRepository {
#Autowired
private PlaceDao placeDao;
#Autowired
private GooglePlaceDao googlePlaceDao;
public List<GooglePlace> findByGroupId(Long groupId) {
// ..
}
public void save(GooglePlace googlePlace) {
// ..
}
public void saveAll(List<GooglePlace> googlePlaces) {
// ..
}
}
#Service
public class UserGroupService {
#Autowired
private GooglePlaceRepository googlePlaceRepository;
#Autowired
private UserGroupRepository userGroupRepository;
#Transactional
public void discoverPlaces(Long groupId) {
final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null)
.orElseThrow(throw new EntityNotFoundException(String.format("User group with id %s not found.", groupId)));
List<PlacesSearchResult> fetched = this.googlePlaces.findPlaces(
userGroup.getLatitude(),
userGroup.getLongitude(),
userGroup.getSearchRadius());
// Either do the mapping here or let GooglePlaces return
// List<GooglePlace> instead of List<PlacesSearchResult>
List<GooglePlace> places = fetched.stream().map(googlePlaceResult -> {
GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);
if (googlePlace != null) {
return googlePlace;
}
Place place = new Place();
place.setLatitude(googlePlaceResult.geometry.location.lat);
place.setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
place.setName(googlePlaceResult.name);
place.setVicinity(googlePlaceResult.vicinity);
googlePlace = new GooglePlace();
googlePlace.setPlace(place);
googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
return googlePlace;
}).collect(Collectors.toList());
this.googlePlaceRepository.saveAll(places);
// Add places to group..
}
}
Summary
I would like to know what I don't see. Am I fighting the framework, or does my data model not make sense and this is why I find myself struggling with this? Or am I still having issues on how the two patterns "Repository" and "DAO" are supposed to be used?
How would one implement this?
I would say you are correct that there are too many repository dependencies in your service. Personally, I try to keep the number of #Autowired dependencies to a minimum and I try to use a repository only in one service and expose its higher level functionality via that service. At our company we call that data sovereignty (in German: Datenhoheit) and its purpose is to ensure that there is only one place in the application where those entities are modified.
From what I understand from your code I would introduce a PlacesService which has all the Dependencies to the PlaceRepository, GooglePlaceRepository and GooglePlaces. If you feel like Service is not the right name you could also call it the PlacesDao, mark it with a Spring #Component annotation and inject all the Repositories, which are by definition collections of things
#Component
public class PlacesDao {
#Autowired
private PlaceRepository placeRepository;
#Autowired
private GooglePlaceRepository googlePlaceRepository;
This service/DAO could offer an API findPlacesForGroup(userGroup) and createNewPlace(...) and thus making your for Loop smaller and more elegant.
On a side note: you can merge your first four lines into just one. Java Optionals support a orElseThrow() method:
UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() ->
new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
I think the foreach does not look like a good approach to me. You're doing way to much for just a single responsibility of a function. I would refactor this to a standart for loop.
Place place = new Place();
place.setLatitude(googlePlaceResult.geometry.location.lat);
place.setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
place.setName(googlePlaceResult.name);
place.setVicinity(googlePlaceResult.vicinity);
place = this.placeRepository.save(place);
This part can easily be a method in a service.
UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new
UserGroupPlace.UserGroupPlaceId();
userGroupPlaceId.setUserGroup(userGroup);
userGroupPlaceId.setPlace(place);
UserGroupPlace userGroupPlace = new UserGroupPlace();
userGroupPlace.setUserGroupPlaceId(userGroupPlaceId);
this.userGroupPlaceRepository.save(userGroupPlace);
That part as well.
googlePlace = new GooglePlace();
googlePlace.setPlace(place);
googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
this.googlePlaceRepository.save(googlePlace);
And this part: I don't understand why your doing this. You could just update the googlePlace instance you loaded from the repo. Hibernate/Transactions are doing the rest for you.

Spring mvc Service Example vs Hibernate Repository Query

I'm sorry to ask many questions these days but i'm on my own.
I would like to know exactly when I have to create a new query in repository and when I have to filter data in service.
For example to get a person by name you can do it in more than 2 differents ways.
public interface PersonRepository extends JpaRepository<Person, Integer> {
#Query("SELECT p FROM Person p WHERE LOWER(p.name) = LOWER(:name)")
Person findOneByName(#Param("name") String name);
}
and
#Service
public class PersonService implements IService<Person,Integer>{
...
public Person findOneByName(String name){
Person personFilter = new Person();
personFilter.setName(name);
ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreCase().withIgnoreNullValues();
Example<Person > example = Example.of(personFilter,matcher);
return personRepository.findOne(example);
}
...
}
I prefere use the first one because it's easier to implement when do we use Example ?
Always use the first one in the simply cases when you can do work by one JPQL (HQL) request.
If you need to build request depending on conditions use the second approach.
Something like:
if (filter.hasName()) {
addNameToExample();
}
if (filter.hasAge()) {
addAgeToExample();
}

Java8 null check collection before add element

I have an entity. Some times the inner collection of info elements not get created. So to implementing correspond method I need null checking.
public class Tag {
...
private Set<ProjectInfo> projectInfoSet;
...
public void addProjectInfo(ProjectInfo projectInfo) {
if (this.projectInfoSet == null) {
this.projectInfoSet = new HashSet<>();
}
this.projectInfoSet.add(projectInfo);
}
}
I believe Java8 provides better solution. For example using Optional class. But i'm not sure if it is good to use Optional in my entity class. Other case possible I do not need here optional, because I need to create projectInfoSet, it should like behavioral pattern strategy.
Can someone recommend the better implementation way or explanations how it could be done in better way.
If you are using jackson you can use a configuration parameter to initialiase it ?
Like this here
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_DEFAULT);
Add initial value to your field and forget about null-check:
public class Tag {
...
private Set<ProjectInfo> projectInfoSet = new HashSet<>();
...
public void addProjectInfo(ProjectInfo projectInfo) {
this.projectInfoSet.add(projectInfo);
}
}

asp.net mvc repository pattern with service layer, when to mix entities in the repositories?

I'm building a new project off the service repository pattern detailed here. It seems to work well in the most basic of examples. In more complex scenarios is it acceptable to mix the objects in the service \ repository layers?. For example say there is a User repository and service and I want to be able to create an audit for the creation of a user, I would think this would go in the service layer.
If I follow the article the service automatically creates the user repository object in the constructor. Adding a audit would mean adding audit CRUD methods to the user repository? Does that make sense to do that?
public UserService(IValidationDictionary validationDictionary, IUserRrepository repository)
{
_validatonDictionary = validationDictionary;
_repository = repository;
}
in my experience you dont need repositories for each entity type. Just create one repository for the whole model, and then use linq queries over it. EF already provides implementation of that repository, you can create a custom interface like shown below and implement it over that repository ..
public interface IDataContext
{
void Add<T>(T entity) where T : BaseEntity;
void Delete<T>(T entity) where T : BaseEntity;
IQueryable<T> Find<T>(Expression<Func<T, bool>> where) where T : BaseEntity;
int SaveChanges()
}
where your base entity is your base class for all repositories.
most of the linq you would write would be pretty straighforward, but for the complicated ones, just write Utility classes
in our implementation the class derived from DbContext implements this interface, and all the auditing is done through the Save Method using the ChangeTracker
A sample implementation of EF 4.2 is below ...
public class MyContext : DbContext, IDataContext
{
static MyContext ()
{
Database.SetInitializer<MyContext >(null);
}
public T GetById<T>(int id) where T : BaseEntity
{
return this.Set<T>().SingleOrDefault(i => i.Id == id);
}
public void Add<T>(T entity) where T : BaseEntity
{
this.Set<T>().Add(entity);
}
public void Delete<T>(T entity) where T : BaseEntity
{
this.Set<T>().Remove(entity);
}
public IQueryable<T> Find<T>(System.Linq.Expressions.Expression<Func<T, bool>> where) where T : BaseEntity
{
return this.Set<T>().Where(where);
}
public override int SaveChanges()
{
this.SetAuditValues();
return base.SaveChanges();
}
private void SetAuditValues()
{
var addedEntries = this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Added);
var currentUser = this.GetCurrentUser();
foreach (var addedEntry in addedEntries)
{
var entity = addedEntry.Entity as BaseEntity;
if (entity != null)
{
entity.CreateDateTime = DateTime.Now;
entity.CreateUser = currentUser;
entity.ModDateTime = DateTime.Now;
entity.ModUser = currentUser;
}
}
var modifiedEntries = this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Modified);
foreach (var modEntry in modifiedEntries)
{
var entity = modEntry.Entity as BaseEntity;
if (entity != null)
{
entity.ModDateTime = DateTime.Now;
entity.ModUser = currentUser;
}
}
}
}
You can surely have one repository/service layer handle more than one entity if it falls within the purpose or domain of that service. Generally in simple examples - you are correct, you don't see this but there is no reason you can include another entity.
Now in regards to your audit, why not just call off to your audit service layer instead of including an audit object (if thats what you meant)

Resources