Java - Spring - Call method with generic parameter - java

I'm trying to create a service similiar to this example that fetches data. Since the GenreType can be anything I created a Registry that maps each GenreType to a specific class.
Ignore the obvious flaw on this example of not being possible to have movies with the same type and the naming not being the most correct but it's just a example to show my problem.
Then I'm levaring Spring to fill this registry but the issues comes when I call the method getData(). It's expecting A extends Genre and I'm passing a Genre so it fails to compile.
Is there a better approach to this?
Thanks
pubic enum GenreType
{
ACTION,
COMEDY
}
public interface Genre
{
}
public class ActionMovie implements Genre
{
}
public class ComedyMovie implements Genre
{
}
public interface Movie<A extends Genre>
{
MovieData getData( A genre );
GenreType getType();
}
#Service
public class Borat implements Movie<ComedyMovie>
{
public MovieData getData( ComedyMovie genre )
{
//...
}
#Override
public GenreType getType()
{
return GenreType.COMEDY;
}
}
#Service
public class MovieRegistry
{
private final Map<GenreType, Movie<?>> movies = new EnumMap<>( GenreType.class );
public MovieRegistry( List<Movie<?>> movies )
{
movies .forEach( movie -> this.movies.putIfAbsent( movie.getType(), movie ) );
}
public Movie<?> get( GenreType genreType )
{
return movies.get( genreType );
}
}
#Service
#RequiredArgsConstructor
public class MovieService
{
private final MovieRegistry movieRegistry;
public MovieData get( GenreType type, Genre genre)
{
Movie<?> movie = movieRegistry.get(type);
return movie.getData( genre ); //Problem here
}
}

Related

CustId is primary key and been used in every class file so how I can fetch data member CustId value from List<CommonDTO> list = new ArrayList<>()

How can I fetch custId from a list which has a generic interface? I tried fetching custId via list.getClass.getField("custId") but unfortunately, the value is not coming.
class Emp implements CommonDto {
private String CustId;
private String EmpId;
private String EmpName;
}
class Student implements CommonDto{
private String CustId;
private String StudentId;
private String StudentName;
}
public interface CommonDto {
}
public class TestApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException {
Emp emp = new Emp(1 , 100, "ayush");
List<Emp> temp = new ArrayList<>() ;
temp.add(emp);
List<CommonDto> commonList = new ArrayList<>();
commonList.add(temp);
for(CommonDto comm : commonList)// is this the right way to fetch CustId
{
// fetch cusId from every domain and add it to new list and return that list
}
}}
In order to access the CustId field you must first recognize that that field does not belong to the CommonDTO class. Therefore, there is no guarantee that a given instance will have that field. If every instance is supposed to have the field then move that field to that class to properly model the data.
Given the existing models, there is a CustId field in both Emp and Student but these are NOT the same field. You can test the data type of a given instance, cast it, and call the appropriate method.
public String getCustId(CommonDTO dto) {
if (dto instanceof Emp) {
return ((Emp)dto).getCustId();
}
if (dto instanceof Student) {
return ((Student)dto).getCustId();
}
return null;
}
If you have leeway to alter the data model then you can move CustId to the parent class (i.e. CommonDTO). Let's assume that his class is provided and you cannot change it. However, you still have leeway to alter the Emp and Student models.
You could also define a common interface that defines the property via method and have each of the classes implement that interface.
public interface Custmor {
public String getCustId();
}
public class Emp implements Customer {
private String custId;
public String getCustId() { return custId; }
...
}
public class Student implements Customer {
private String custId;
public String getCustId() { return custId; }
...
}
Then you can handle the data via the interface.
public String getCustId(CommonDTO dto) {
if (dto instanceof Customer) {
return ((Customer)dto).getCustId();
}
return null;
}
Alternatively, you could create a Customer class that extends the CommonDTO and serves as a base class for both of these...
public class Customer extends CommonDTO {
private custId;
public String getCustId() { return custId; }
...
}
And alter the Emp and Student classes to extend this...
public class Emp extends Customer { ... }
public class Student extends Customer { ... }
In either case (interface or common base class), you can then use instanceof to determine if a given CommonDTO instance is a Customer and cast it to access the method. You can use the Stream API to easily pull the custIds from the Customers in a List<CommonDTO>:
List<String> customerIds = commonList.stream()
.filter(c -> c instanceof Customer) // filter out the non-Customer elements out of the streawm
.map(c -> (Customer)c) // cast the CommonDTO to a Customer
.map(c -> c.getCustId()) // get the custId
.collect(Collectors.toList()); // capture the custIds in a List
p.s. suggest that you adhere to Java naming conventions (e.g. variable names begin with lower case letter).

Generic Mapper function in MapStruct

Friends,
Here are my Java objects
#Data
public class CarDto {
private String make;
private String model;
private int year;
}
#Data
public class Car {
private MakeEnum make;
private String model;
private int year;
}
For consuming, I need to do something like this
#Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
Car toModel(CarDto carDto);
CarDto toDto(Car carModel);
}
// Using mapper
Car carModel = CarMapper.INSTANCE.toModel(carDto);
But I am looking a solution, where I could do this:
Car carModel = Mapper.map(carDto, Car.class);
How do do this? Didn't find an example where I can dynamically map based on a Type. I found this method very handy in both ModelMapper & Google gson. Appreciate your help!
If I understand you correctly, you require a kind of repo.
Another option would be to look at sprint and the new MapStruct spring integration, recently developed: https://github.com/mapstruct/mapstruct-spring-extensions. It was designed as a follow up of this question.
There's an example in the example repo. It's not straightforward though: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-mapper-repo
Before calling the mapping, you have to setup and interface of MapStruct like this:
#Mapper(componentModel = "spring")
public interface MapStructMapper {
ObjectDto objectToObjectDto(Object object);
}
Then an implementation of it :
#Component
public class MapStructMapperImpl implements MapStructMapper {
#Override
public ObjectDto objectToObjectDto(Object object) {
if ( object == null ) { return null; }
ObjectDto objectDto = new ObjectDto();
objectDto.setId( object.getId() );
objectDto.setName( object.getName() );
return objectDto;
}
and then, you just have to inject this interface in the controller and invoke the repository like this:
#RequestMapping("/objects")
public class ObjectController {
private MapStructMapper mapstructMapper;
private ObjectRepository objectRepository;
#Autowired
public ObjectController(
MapStructMapper mapstructMapper,
ObjectRepository objectRepository
) {
this.mapstructMapper = mapstructMapper;
this.objectRepository = objectRepository;
}
#GetMapping("/{id}")
public ResponseEntity<ObjectDto> getById(#PathVariable(value = "id") int id){
return new ResponseEntity<>(
mapstructMapper.objectToObjectDto(
objectRepository.findById(id).get()
),
HttpStatus.OK
);
}
}
Of course, you can call a service/serviceImpl instead of a direct call to the repository but it's to be as simple as possible. :)

How to chain queries with rxjava and room

I need to fill the fields of an object in order to post it to an API.
I am using rxjava and room but my chain of orders is failling
My daos
#Dao
abstract public class PokemonDao implements BaseDao<Pokemon>{
#Query("SELECT * FROM pokemons ORDER BY id ASC")
abstract public Flowable<List<Pokemon>> getAll();
}
#Dao
abstract public class NoteDao implements BaseDao<Note>{
#Query("SELECT * FROM notes WHERE idPokemon = :idPokemon ORDER BY registerDate DESC")
abstract public Flowable<List<Note>> getNotes(int idPokemon);
}
I need to create an object that has the data of the pokemon with a list of notes associated
I did the following on my viewmodel
pokemonRepository.getFavourites()
.toObservable()
.flatMap(new Function<List<Pokemon>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(List<Pokemon> favourites) throws Exception {
return Observable.fromIterable(favourites);
}
})
.flatMap(new Function<Object, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(Object o) throws Exception {
return getNotesObservable((Favourite) o);
}
})
.toList()
.toObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new SingleObserver<List<Object>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(List<Object> objects) {
}
#Override
public void onError(Throwable e) {
}
})
I also use this method
private Observable<Object> getNotesObservable(Pokemon favourite) {
Observable<Object> lolo = noteRepository.getNotes(Integer.parseInt(favourite.getId()))
.map(new Function<List<Note>, Object>() {
#Override
public Favourite apply(List<Note> notes) throws Exception {
favourite.notesList= notes;
return favourite;
}
})
.toObservable();
return lolo;
}
My problem is that on the subscribeWith onNext method is never called.
My goal it that when onNext is called it should have a list of pokemon and each pokemon should have their notes
Thanks
Below I describe Room-ish way for your task without RxJava
Let's say you have these entities:
#Entity
public class Pokemon {
#PrimaryKey public int id;
public String name;
// other fields
........
}
#Entity
public class Note {
#PrimaryKey public int noteId;
public int pokemonId;
// other fields
........
}
Then you can add another class (it's just a class with no connection to SQLite):
public class PokemonWithNotes {
#Embedded public Pokemon pokemon; // here you'll get your pokemon
#Relation(
parentColumn = "id",
entityColumn = "pokemonId"
)
public List<Note> notesList; // here you'll het your notes' list
}
and add method to your dao:
#Transaction
#Query("SELECT * FROM Pokemon")
public List<PokemonWithNotes> getPokemonListWithNotes();
Room orders to this method to get both Pokemons and Notes and connect them (without two queries)
Using this method you'll get your List with Pokemons and notes.

Accept generic List as parameter and use it base on its type

I'm developing a website for my company, and I use Spring as my backend.
There is a situation now, where I need to use one of my Utils method twice, but for different DAOs.
In order to avoid code duplication, I was wondering how can I use Java Generics in order to make this method usable for both cases. The method just count one of the fields which is common for both DAOs.
Util method :
SeverityCount calculateSeveritiesCount(List<?> events){
if(null == events){
return new SeverityCount();
}
if(events.get(1) instanceof EventDAO){
events = (List<EventDAO>)events;
}
else if (events.get(1) instanceof EventsByAreaDAO) {
events = (List<EventsByAreaDAO>)events;
}
Map<String, Long> severityCountMap = events.stream().collect(
Collectors.groupingBy(
EventDAO::getSeverity, //It should be EventDAO or EventsByAreaDAO. both has severity field.
Collectors.counting())
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
Event DAO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "events")
public class EventDAO {
#Id #Column(name = "uid")
private String uID;
private String date;
private String severity;
}
Area DAO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "events")
public class EventsByRegionDAO {
#Id #Column(name = "uid")
private String uID;
private String date;
private String type;
private String severity;
private String area;
private String server;
}
This is how I call it from the service:
SeverityCount severitiesCount = Utils.calculateSeveritiesCount(eventsList); //EventsList could be list of EventDAO or EventsByAreaDAO
You can change the method to
SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events)
where SeverityCalculable
interface SeverityCalculable {
String getSeverity(); // implemente getter in all subclasses
}
Have all your relevant classes implement this interface.
public class EventDAO implements SeverityCalculable {
// ...
#Override
public String getSeverity() {
return this.severity;
}
}
Now in your method, remove the casts and it should become something like this:
SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events) {
if(null == events){
return new SeverityCount();
}
Map<String, Long> severityCountMap = events.stream().collect(
Collectors.groupingBy(
SeverityCalculable::getSeverity,
Collectors.counting()
)
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
Since both DAO's have a severity property, they could potentially implement a common interface, say SeverityAware:
public interface SeverityAware {
public String getSeverity();
}
public class EventsByRegionDAO implements SeverityAware { .. }
public class EventDAO implements SeverityAware { .. }
Further, your method could now accept subtypes of this interface:
SeverityCount calculateSeveritiesCount(List<? extends SeverityAware> events){
if (null == events){
return new SeverityCount();
}
Map<String, Long> severityCountMap = events.stream().collect(Collectors.groupingBy(
SeverityAware::getSeverity,Collectors.counting())
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}

How to use enums to create generic API for Search and Filter of POJOs in Java?

Lets say you have a particular enum called Field:
public enum Field {
ALBUM,
DESCRIPTION
}
And another enum called:
public enum Operator {
CONTAINS,
EQUALS,
LESS_THAN,
GREATER_THAN
}
And you have a corresponding Java interface called Music
public interface Music {
String getAlbum();
String getDescription();
}
Which the implementation looks liks this:
public class MusicImpl implements Music {
#Override
public String getYear() {
return Field.Year.toString();
}
#Override
public String getDescription() {
return Field.DESCRIPTION.toString();
}
#Override
public Object getField(Field field) {
Object myObject = field.getClass();
return myObject;
}
}
How would you use this API to implement the following two methods?
public class MyApp {
#Override
public List<Music> searchMusic(String query) {
// What to put in this?
}
#Override
public List<Music> filterMusic(Field field, Operator op, String query) {
// What to put in?
}
}

Categories