Java 带有DTO和泛型的REST子资源,获取类型擦除错误

Java 带有DTO和泛型的REST子资源,获取类型擦除错误,java,generics,jax-rs,java-ee-7,type-erasure,Java,Generics,Jax Rs,Java Ee 7,Type Erasure,我正在研究一个相当复杂的模型来处理任何DTO/实体类型。每个DTO都有自己的REST端点,但将使用一个通用子资源从数据库读/写。我得到了一个类型擦除错误,但我不知道如何解决它 首先,这里是一个示例实体和相应的DTO: 实体: @Entity public class MyEntity extends AbstractEntity implements Serializable { private static final long serialVersionUID = 1L;

我正在研究一个相当复杂的模型来处理任何DTO/实体类型。每个DTO都有自己的REST端点,但将使用一个通用子资源从数据库读/写。我得到了一个类型擦除错误,但我不知道如何解决它

首先,这里是一个示例实体和相应的DTO:

实体

@Entity
public class MyEntity extends AbstractEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(nullable = false)
    @NotNull(message = "ID Required!")
    private Integer id;

    @Basic(optional = false)
    @Column(nullable = false)
    @NotNull(message = "Name Required!")
    private String name;

}
public interface DTO<Entity extends AbstractEntity, PrimaryKey> {
   /* utility methods here */
}
@XmlRootElement
public class MyEntityDTO implements DTO<MyEntity, Integer> {

    private Integer id;
    private String name;
}
@Path("/stuff")
@RequestScoped
public class StuffFacade {

    @Inject
    EntityMappedManager entityMappedManager;

    @Path("/my-items")
    public StuffFacade.EntityFacade<MyEntity, Integer, MyEntityDTO> myEntityFacade() {
        return new StuffFacade.EntityFacade<>(getAccessor(), MyEntityDTO::new);
    }

    @Path("/your-items")
    public StuffFacade.EntityFacade<YourEntity, String, YourEntityDTO> yourEntityFacade() {
        return new StuffFacade.EntityFacade<>(getAccessor(), YourEntityDTO::new);
    }


    @Produces
    @Override
    public <E extends AbstractEntity, P> DatastoreAccessor<E, P> getAccessor() {
        return new DatastoreAccessor<E, P>() {

            @Override
            public List<E> findAll(QueryParams queryParams) {
                return entityMappedManager.findAll(getEntityClass());
            }

            @Override
            public Optional<E> findById(P id) {
                return Optional.ofNullable(entityMappedManager.findById(getEntityClass(), id));
            }

            @Override
            public E create(E entity) {
                return entityMappedManager.save(entity);
            }

            @Override
            public E update(E entity) {
                return entityMappedManager.save(entity);
            }

            @Override
            public E remove(E entity) {
                return entityMappedManager.remove(entity);
            }

        };
    }

        public class EntityFacade<Entity extends AbstractEntity, PrimaryKey, DTOType extends DTO<Entity, PrimaryKey>> extends DTOFacade<Entity, PrimaryKey, DTOType> {

            private final DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor;
            private final Function<Entity, DTOType> dtoMapper;

            public EntityFacade(DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor, Function<Entity, DTOType> dtoMapper) {
                this.datastoreAccessor = datastoreAccessor;
                this.dtoMapper = dtoMapper;
            }

            @Override
            public DatastoreAccessor<Entity, PrimaryKey> getDatastoreAccessor() {
                return this.datastoreAccessor;
            }

            @Override
            public GenericEntity<ListMetadata<DTOType>> getGenericEntity(List<DTOType> list) {
                return new GenericEntity<List<DTOType>>(list) {
                };
            }

            @Override
            public Function<Entity, DTOType> getDTOMapper() {
                return this.dtoMapper;
            }

        }

}
 public static abstract class DTOFacade<Entity extends AbstractEntity, PrimaryKey, DTOType extends DTO<Entity, PrimaryKey>> {

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    public Response findAll(@Context UriInfo uriInfo) {
        QueryParams queryParams = new QueryParams(uriInfo.getQueryParameters());
        List<Entity> results = getDatastoreAccessor().findAll(queryParams);
        List<DTOType> converted = new ListMetadata<>(
                results.getList().stream()
                .map(getDTOMapper())
                .collect(Collectors.toList()), results.getParentSize());
        return Response.ok(getGenericEntity(converted)).build();
    }

    @GET
    @Path("/{id}")
    @Produces({MediaType.APPLICATION_JSON})
    public Response findById(@PathParam("id") PrimaryKey id) {
        Optional<Entity> optionalEntity = getDatastoreAccessor().findById(id);
        if (!optionalEntity.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(optionalEntity.map(getDTOMapper()).get()).build();
    }

    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public Response create(DTOType entity) {
        DTOType created = getDTOMapper().apply(getDatastoreAccessor().create(toObjectModel(entity)));
        return Response.ok(created).build();
    }

    @PUT
    @Path("/{id}")
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public Response update(DTOType entity) {
        DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor = getDatastoreAccessor();
        Optional<Entity> e = datastoreAccessor.findById(entity.getId());
        if (!e.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(getDTOMapper().apply(datastoreAccessor.update(toObjectModel(entity)))).build();
    }

    @DELETE
    @Path("/{id}")
    @Produces({MediaType.APPLICATION_JSON})
    public Response delete(@PathParam("id") PrimaryKey id) {
        DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor = getDatastoreAccessor();
        Optional<Entity> entity = datastoreAccessor.findById(id);
        if (!entity.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(getDTOMapper().apply(datastoreAccessor.remove(entity.get()))).build();
    }

    public abstract DatastoreAccessor<Entity, PrimaryKey> getDatastoreAccessor();

    public abstract GenericEntity<List<DTOType>> getGenericEntity(List<DTOType> list);

    public abstract Function<Entity, DTOType> getDTOMapper();

    public Entity toObjectModel(DTOType e) {
        return e.toObjectModel();
    }
}
public interface DatastoreAccessor<Entity extends AbstractEntity, PrimaryKey> {

    List<Entity> findAll(QueryParams queryParams);

    Optional<Entity> findById(PrimaryKey id);

    Entity create(Entity entity);

    Entity update(Entity entity);

    Entity remove(Entity entity);

    default Class<Entity> getEntityClass() {
        return ((Class<Entity>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}
@Stateless
@LocalBean
public class EntityMappedManager {

    @PersistenceContext
    public EntityManager em;

    public <T extends AbstractEntity, P> T findById(Class<T> clazz, P id){
        return em.find(clazz, id);
    }


    public <T extends AbstractEntity> List<T> findAll(Class<T> clazz) {
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<T> query = builder.createQuery(clazz);
        Root<T> root = query.from(clazz);
        query.select(root);
        return em.createQuery(query).getResultList();
    }
    // OTHER METHODS REMOVED FOR BREVITY
}
数据到实用程序接口

@Entity
public class MyEntity extends AbstractEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(nullable = false)
    @NotNull(message = "ID Required!")
    private Integer id;

    @Basic(optional = false)
    @Column(nullable = false)
    @NotNull(message = "Name Required!")
    private String name;

}
public interface DTO<Entity extends AbstractEntity, PrimaryKey> {
   /* utility methods here */
}
@XmlRootElement
public class MyEntityDTO implements DTO<MyEntity, Integer> {

    private Integer id;
    private String name;
}
@Path("/stuff")
@RequestScoped
public class StuffFacade {

    @Inject
    EntityMappedManager entityMappedManager;

    @Path("/my-items")
    public StuffFacade.EntityFacade<MyEntity, Integer, MyEntityDTO> myEntityFacade() {
        return new StuffFacade.EntityFacade<>(getAccessor(), MyEntityDTO::new);
    }

    @Path("/your-items")
    public StuffFacade.EntityFacade<YourEntity, String, YourEntityDTO> yourEntityFacade() {
        return new StuffFacade.EntityFacade<>(getAccessor(), YourEntityDTO::new);
    }


    @Produces
    @Override
    public <E extends AbstractEntity, P> DatastoreAccessor<E, P> getAccessor() {
        return new DatastoreAccessor<E, P>() {

            @Override
            public List<E> findAll(QueryParams queryParams) {
                return entityMappedManager.findAll(getEntityClass());
            }

            @Override
            public Optional<E> findById(P id) {
                return Optional.ofNullable(entityMappedManager.findById(getEntityClass(), id));
            }

            @Override
            public E create(E entity) {
                return entityMappedManager.save(entity);
            }

            @Override
            public E update(E entity) {
                return entityMappedManager.save(entity);
            }

            @Override
            public E remove(E entity) {
                return entityMappedManager.remove(entity);
            }

        };
    }

        public class EntityFacade<Entity extends AbstractEntity, PrimaryKey, DTOType extends DTO<Entity, PrimaryKey>> extends DTOFacade<Entity, PrimaryKey, DTOType> {

            private final DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor;
            private final Function<Entity, DTOType> dtoMapper;

            public EntityFacade(DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor, Function<Entity, DTOType> dtoMapper) {
                this.datastoreAccessor = datastoreAccessor;
                this.dtoMapper = dtoMapper;
            }

            @Override
            public DatastoreAccessor<Entity, PrimaryKey> getDatastoreAccessor() {
                return this.datastoreAccessor;
            }

            @Override
            public GenericEntity<ListMetadata<DTOType>> getGenericEntity(List<DTOType> list) {
                return new GenericEntity<List<DTOType>>(list) {
                };
            }

            @Override
            public Function<Entity, DTOType> getDTOMapper() {
                return this.dtoMapper;
            }

        }

}
 public static abstract class DTOFacade<Entity extends AbstractEntity, PrimaryKey, DTOType extends DTO<Entity, PrimaryKey>> {

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    public Response findAll(@Context UriInfo uriInfo) {
        QueryParams queryParams = new QueryParams(uriInfo.getQueryParameters());
        List<Entity> results = getDatastoreAccessor().findAll(queryParams);
        List<DTOType> converted = new ListMetadata<>(
                results.getList().stream()
                .map(getDTOMapper())
                .collect(Collectors.toList()), results.getParentSize());
        return Response.ok(getGenericEntity(converted)).build();
    }

    @GET
    @Path("/{id}")
    @Produces({MediaType.APPLICATION_JSON})
    public Response findById(@PathParam("id") PrimaryKey id) {
        Optional<Entity> optionalEntity = getDatastoreAccessor().findById(id);
        if (!optionalEntity.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(optionalEntity.map(getDTOMapper()).get()).build();
    }

    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public Response create(DTOType entity) {
        DTOType created = getDTOMapper().apply(getDatastoreAccessor().create(toObjectModel(entity)));
        return Response.ok(created).build();
    }

    @PUT
    @Path("/{id}")
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    public Response update(DTOType entity) {
        DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor = getDatastoreAccessor();
        Optional<Entity> e = datastoreAccessor.findById(entity.getId());
        if (!e.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(getDTOMapper().apply(datastoreAccessor.update(toObjectModel(entity)))).build();
    }

    @DELETE
    @Path("/{id}")
    @Produces({MediaType.APPLICATION_JSON})
    public Response delete(@PathParam("id") PrimaryKey id) {
        DatastoreAccessor<Entity, PrimaryKey> datastoreAccessor = getDatastoreAccessor();
        Optional<Entity> entity = datastoreAccessor.findById(id);
        if (!entity.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(getDTOMapper().apply(datastoreAccessor.remove(entity.get()))).build();
    }

    public abstract DatastoreAccessor<Entity, PrimaryKey> getDatastoreAccessor();

    public abstract GenericEntity<List<DTOType>> getGenericEntity(List<DTOType> list);

    public abstract Function<Entity, DTOType> getDTOMapper();

    public Entity toObjectModel(DTOType e) {
        return e.toObjectModel();
    }
}
public interface DatastoreAccessor<Entity extends AbstractEntity, PrimaryKey> {

    List<Entity> findAll(QueryParams queryParams);

    Optional<Entity> findById(PrimaryKey id);

    Entity create(Entity entity);

    Entity update(Entity entity);

    Entity remove(Entity entity);

    default Class<Entity> getEntityClass() {
        return ((Class<Entity>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}
@Stateless
@LocalBean
public class EntityMappedManager {

    @PersistenceContext
    public EntityManager em;

    public <T extends AbstractEntity, P> T findById(Class<T> clazz, P id){
        return em.find(clazz, id);
    }


    public <T extends AbstractEntity> List<T> findAll(Class<T> clazz) {
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<T> query = builder.createQuery(clazz);
        Root<T> root = query.from(clazz);
        query.select(root);
        return em.createQuery(query).getResultList();
    }
    // OTHER METHODS REMOVED FOR BREVITY
}

除了非致命警告之外,实际异常是由完全不同的原因引起的。它来自
PrimaryKey
,不能作为
@PathParam
注入。查看javadoc for
@PathParam
,了解有关使用自定义类型注入的规则。如果查看
DTOFacade
,您将看到
PrimaryKey
实际上是一个类型参数,它设置在
EntityFacade
(API的嵌入式类)上,它是通过类型推断定义的,返回类型为
myEntityFacade()
上的
StuffFacade.EntityFacade
。我不知道
@PathParam
有什么关系。很抱歉,我没有注意到这一点。让它工作的一种方法是创建EntityFacade的子类,传递类型参数,然后返回该子类型。我将尝试一下。我无法让它工作,所以我最终将该类传递给DTOFCADE构造函数。