Java 带有DTO和泛型的REST子资源,获取类型擦除错误
我正在研究一个相当复杂的模型来处理任何DTO/实体类型。每个DTO都有自己的REST端点,但将使用一个通用子资源从数据库读/写。我得到了一个类型擦除错误,但我不知道如何解决它 首先,这里是一个示例实体和相应的DTO: 实体: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;
@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构造函数。