MapStruct bulk conversion with #aftermapping - mapstruct

I want to apply a decoration using a dedicated #AfterMapping after the single item conversion of a DTO and another dedicated #AfterMapping when dealing with its collection conversion flavor but not both.
public abstract CatUI convert(Cat cat);
public abstract List<CatUI> convert(List<Cat> cats);
#AfterMapping
public void populateCatName(Cat cat, #MappingTarget CatUI catUI) {
String name = _someRemoteService.getCatName(catUI.getId());
catUI.setName(name);
}
#AfterMapping
public void populateCatNames(List<Cat> cats, #MappingTarget List<CatUI> catUIs) {
Map<Integer,String> idToNameMap = _someRemoteService.getCatNames(catUIs.stream().map((c) -> c.getId() ).collect(Collectors.toList());
catUIs.forEach((c) -> c.setName(idToNameMap(c.getId())));
}
I don't want populateCatName to be called when dealing with List conversion and hence duplicate my decoration.
Anyway to do this?

With 1.1.0.Final you will have to define 2 entry points (Mappers) one with the conversion on list and the other one without it.
I would suggest you try out the new 1.2.0.Beta2. With that one you can use the new #Context.
You can have an interface like:
public interface CatMappingContext {
#AfterMapping
public default void populateCatName(Cat cat, #MappingTarget CatUI catUI) {
//nothing per default
}
#AfterMapping
public void populateCatNames(List<Cat> cats, #MappingTarget List<CatUI> catUIs) {
//nothing per default
}
}
And 2 implementations:
public class SingleMappingContext implements CatMappingContext {
#AfterMapping
public void populateCatName(Cat cat, #MappingTarget CatUI catUI) {
String name = _someRemoteService.getCatName(catUI.getId());
catUI.setName(name);
}
}
public class ListMappingContext implements CatMappingContext {
#AfterMapping
public void populateCatNames(List<Cat> cats, #MappingTarget List<CatUI> catUIs) {
Map<Integer,String> idToNameMap = _someRemoteService.getCatNames(catUIs.stream().map((c) -> c.getId() ).collect(Collectors.toList());
catUIs.forEach((c) -> c.setName(idToNameMap(c.getId())));
}
}
Finally your mapper can look like:
public interface CatMapper {
public CatUI convert(Cat cat, #Context CatMappingContext context);
public List<CatUI> convert(List<Cat> cats, #Context CatMappingContext context);
}
You will then need to call your methods with the correct instance of the context SingleMappingContext or the ListMappingContext.

Related

How to change my DAO Bean from EJB to pure CDI?

I want to reuse my AbstractDAO in a new project, except this time I don't want to use EJB annotations - just CDI ones.
So far, I've been using it like this:
public abstract class AbstractDAO<T> {
#PersistenceContext(unitName = "myUnit")
private EntityManager entityManager;
private Class<T> entityClass;
public AbstractDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected EntityManager getEntityManager() {
return entityManager;
}
public void save(T entity) {
entityManager.persist(entity);
}
public void update(T entity) {
entityManager.merge(entity);
}
public void remove(T entity) {
entityManager.remove(entityManager.merge(entity));
}
public T findById(Object id) {
return entityManager.find(entityClass, id);
}
public List<T> findBy(String attrName, Object attrValue) {
// Impl here
}
// [...] Many more search methods
}
And I've been creating a DAO for each Entity, like this for example:
#Stateless
public class UserDAO extends AbstractDAO<User> {
public UserDAO() {
super(User.class);
}
public User findByUsername(String username) {
if (username != null) {
return super.findOneBy("username", username.toLowerCase());
}
return null;
}
}
Now I would like to get rid of the #Stateless annotation. But simply replacing it with a #RequestScoped one won't work because of the non-private constructor with no parameters requirement of JSR-346
How can I refactor my DAO to a pure CDI one ?
Two issues here: CDI beans are not transaction aware by default -unlike EJBs, so you will have to use #Transactional qualifier if you want to do saves/updates...
Second, your no-arg constructor: you only need to pass the entity class to your abstract class even though you also specify it as a generic argument. You can infer the actual class like this:
public class AbstractDAO<T> {
private transient Class<T> entityClass;
#SuppressWarnings("unchecked")
public AbstractDAO() {
Type generSuperCls = getClass().getGenericSuperclass();
if (generSuperCls instanceof Class) {
generSuperCls = ((Class<?>) generSuperCls).getGenericSuperclass();
}
ParameterizedType parameterizedType = (ParameterizedType) generSuperCls;
Type type = parameterizedType.getActualTypeArguments()[0];
if (type instanceof Class) {
this.entityClass = (Class<T>) type;
} else if (type instanceof ParameterizedType) {
this.entityClass = (Class<T>) ((ParameterizedType) type).getRawType();
}
}
#PersistenceContext
private EntityManager em;
public T getById(Object id) throws ServiceException {
return getEm().find(entityClass, id);
}
// other methods follow
}
As a side note, why do you want to get rid of EJBs? Benchmarks show you get better performance using pooled slsb than cdi and they together very well(every EJB beans is also a CDI bean in jee container).

Mapstruct passing enclosed object to the mapping method

Is there a way to pass the enclosed object reference as a parameter to the mapping method?
public class Car {
public int id;
public Person driver;
}
public class Person {
public int carId;
}
I need a reference to the enclosing car object when mapping the person object in the personToPersonDtoAfterMapping custom method.
#Mapper
public abstract class CarMapper {
public abstract CarDto carToCarDto(Car car);
public abstract PersonDto personToPersonDto(Person person);
#AfterMapping
protected void personToPersonDtoAfterMapping(Person person, #MappingTarget PersonDto dto, Car enclosedCar) {
dto.setCarId(enclosedCar.getId())
}
}
Simply use #Context annotation. Did you try?
#Mapper
public abstract class CarMapper {
public abstract CarDto carToCarDto(Car car);
public abstract PersonDto personToPersonDto(Person person, #Context Car enclosedCar);
#AfterMapping
protected void personToPersonDtoAfterMapping(Person person, #MappingTarget PersonDto dto, #Context Car enclosedCar) {
dto.setCarId(enclosedCar.getId())
}
}

Injecting a Factory that accepts a Parameter with AutoFac

I've read over several examples that were more complex then I needed and I'm having trouble distilling this down to a simple, concise pattern.
Let's say I have an interface names ICustomService and multiple implementations of ICustomService. I also have a class Consumer that needs to determine at run time which ICustomService to use based upon a parameter.
So I create a classes as follows:
public class Consumer
{
private CustomServiceFactory customServiceFactory;
public Consumer(CustomServiceFactory _customServiceFactory)
{
customServiceFactory = _customServiceFactory;
}
public void Execute(string parameter)
{
ICustomService Service = customServiceFactory.GetService(parameter);
Service.DoSomething();
}
}
public class CustomServiceFactory
{
private IComponentContext context;
public CustomServiceFactory(IComponentContext _context)
{
context = _context;
}
public ICustomService GetService(string p)
{
return context.Resolve<ICustomService>(p); // not correct
}
}
public class ServiceA : ICustomService
{
public void DoSomething()
{
}
}
public class ServiceB : ICustomService
{
public void DoSomething()
{
}
}
Is there an advantage to having my factory implement an interface? How do I fix my factory and register these classes with Autofac so that Consumer.Execute("A") calls DoSomething on WorkerA and Consumer.Execute("B") calls DoSomething on WorkerB?
Thank you
You would register your implementations of ICustomService with keys. For example:
builder.RegisterType<FooService>.Keyed<ICustomService>("someKey");
builder.RegisterType<BarService>.Keyed<ICustomService>("anotherKey");
and then your factory method would be:
public ICustomService GetService(string p)
{
return context.ResolveKeyed<ICustomService>(p);
}
But, you can take this a step further and decouple CustomServiceFactory from IComponentContext:
public class CustomServiceFactory
{
private Func<string, ICustomService> _create;
public CustomServiceFactory(Func<string, ICustomService> create)
{
_create = create;
}
public ICustomService GetService(string p)
{
return _create(p);
}
}
which you would register like so:
builder.Register(c => {
var ctx = c.Resolve<IComponentContext>();
return new CustomServiceFactory(key => ctx.ResolveKeyed<ICustomService>(key));
});
And at that point, assuming CustomServiceFactory doesn't have any other behavior that was omitted for the question, then you as might as well just use and register Func<string, ICustomService> directly.

autofac registergeneric error "sequence contains no element"

Using Autofac, I tried to use my generic type :
public interface IRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(long Id);
T GetById(string Id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
to register my generic class :
public abstract class RepositoryBase<T> where T : class
{
private iMOSSContainer dataContext;
private readonly IDbSet<T> dbset;
protected RepositoryBase()
{
DatabaseFactory = new DatabaseFactory();
dbset = DataContext.Set<T>();
}
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected iMOSSContainer DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual void Update(T entity)
{
dbset.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
dbset.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
dbset.Remove(obj);
}
public virtual T GetById(long id)
{
return dbset.Find(id);
}
public virtual T GetById(string id)
{
return dbset.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return dbset.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return dbset.Where(where).FirstOrDefault<T>();
}
}
this way :
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(RepositoryBase<>)).As(typeof(IRepository<>)); //.InstancePerLifetimeScope();
Container = builder.Build();
And returns an error "sequence contains no element" when building a container.
Did i do something wrong with the code? I already tried to search for an answer to no avail. So, your help would be pretty much appreciated.
Ok, then i changed the RepositoryBase class like this :
public class RepositoryBase<T> : IRepository<T> where T : class
{
private iMOSSContainer dataContext;
private readonly IDbSet<T> dbset;
public RepositoryBase()
{
DatabaseFactory = new DatabaseFactory();
dbset = DataContext.Set<T>();
}
So :
i use the "iRepository" interface (and move the "where T : class"
statement at the end of the line)
i remove the "abstract" class (because it leads to another error)
set the default constructor to "public" (because it leads to another
error, as well)
And everything is working good right now.
Your RepositoryBase type is abstract, therefore it can't be registered. You need to register non-abstract types. I'm guessing what you're intending to do is register all subtypes of RepositoryBase.

UnitOfWork and Entity Framework Contexts

So the problem I am trying to solve is this; We are using Entity Framework to access our Oracle database that has 1200-1500 tables. Now mind you we are not accessing them all, but possibly could have 800+ to access. We are using the UnitOfWork --> Repository --> Service pattern and that works great, but we are trying to figure out if we should have one big DbContext, or multiple little contexts that are specific to the task at hand.
Our UnitOfWork is setup using an EFUnitOfWorkBase like so:
public abstract class EFUnitOfWorkBase : IUnitOfWork
{
private bool isDisposed = false;
public DbContextBase Context { get; set; }
protected EFUnitOfWorkBase(DbContextBase context)
{
Context = context;
}
public int Commit()
{
return Context.SaveChanges();
}
public void Dispose()
{
if (!isDisposed)
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
isDisposed = true;
if (disposing)
{
if (this.Context != null)
this.Context.Dispose();
}
}
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : Common.EntityBase<TEntity>
{
return new Repository<TEntity>(this);
}
}
Any unit of work we create extends that base one and provides the context like so:
public class EmployeeDirectoryUnitOfWork : EFUnitOfWorkBase
{
public EmployeeDirectoryUnitOfWork(string connectionString)
: base(new EmployeeDirectoryContext(connectionString))
{
}
}
The DbContext is passed a connection string through the unit of work.
The Repository looks like this:
public abstract class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContextBase Context;
protected DbSet<TEntity> EntitySet;
public RepositoryBase(EFUnitOfWorkBase unitOfWork)
{
Enforce.ArgumentNotNull(unitOfWork, "unitOfWork");
Context = unitOfWork.Context;
EntitySet = Context.Set<TEntity>();
}
public TEntity Add(TEntity entity)
{
Enforce.ArgumentNotNull(entity, "entity");
return EntitySet.Add(entity);
}
public TEntity Attach(TEntity entity)
{
Enforce.ArgumentNotNull(entity, "entity");
return EntitySet.Attach(entity);
}
public TEntity Delete(TEntity entity)
{
Enforce.ArgumentNotNull(entity, "entity");
return EntitySet.Remove(entity);
}
public System.Linq.IQueryable<TEntity> Query()
{
return EntitySet.AsQueryable();
}
public TEntity Save(TEntity entity)
{
Enforce.ArgumentNotNull(entity, "entity");
Attach(entity);
Context.MarkModified(entity);
return entity;
}
}
Any suggestions on how to best handle this situation?
In such a case when you have a large application like this, I think you should probably go for a more Domain Driven Design approach and split the contexts into some separate, bounded contexts. This way when later developers are adding features to the program they will be confined to only being able to access certain tables depending on which context they will be using there.
For better information, Julie Lerman recently came out with a course on Pluralsight about Entity Framework in the Enterprise that's really good. She posted a small clip of it (actually about bounded contexts) on this site. It's a very good course, and I highly recommend it, especially for what you appear to be doing.