从实体到 DTO 的转换

Posted

技术标签:

【中文标题】从实体到 DTO 的转换【英文标题】:Transition from Entity to DTO 【发布时间】:2018-03-09 11:53:09 【问题描述】:

我想在所有类中使用 DTO 对象,但是当我从任何地方将 User> 更改为 UserDTO> 时出现错误。

我的错误日志是;

org.springframework.beans.factory.UnsatisfiedDependencyException: 创建名为“userService”的 bean 时出错:不满足的依赖关系 通过字段“userRepository”表示;嵌套异常是 org.springframework.beans.factory.BeanCreationException:错误 创建名为“userRepository”的 bean:调用 init 方法 失败的;嵌套异常是 java.lang.IllegalArgumentException: Not a 托管类型:类 de.javatar81.examples.domain.UserDTO

引起:org.springframework.beans.factory.BeanCreationException: 创建名为“userRepository”的 bean 时出错:调用 init 方法失败;嵌套异常是 java.lang.IllegalArgumentException: 不是托管类型:类 de.javatar81.examples.domain.UserDTO

原因:java.lang.IllegalArgumentException:不是托管类型: 类 de.javatar81.examples.domain.UserDTO

User.class;

    @Entity
    @Table(name="USER")
    public class User implements Serializable 

        private static final long serialVersionUID = -7860243025833384447L;

        @Id
        private Long id;

        private String login;
        private String firstName;
        private String lastName;
        private Date dayOfBirth;
        @ManyToOne
        private User manager;

//setters and getters

    

UserDTO.class;

    public class UserDTO implements Serializable 

        private static final long serialVersionUID = -7860243025833384447L;

        private Long id;
        private String login;
        private String firstName;
        private String lastName;
        private Date dayOfBirth;
        private String city;
        private String district;

//setters and getters

    

UserRepository.class;

public interface UserRepository extends PagingAndSortingRepository<UserDTO, Long>, JpaSpecificationExecutor<UserDTO>, JpaRepository<UserDTO, Long>, Repository<UserDTO, Long>
    @Query(value = "select * from user where login like :filters% order by login \n-- #pageable\n",
    countQuery = "select count(*) from user where login like :filters%",
    nativeQuery = true)
    Page<UserDTO> findByLogin(@Param("filters") String filters, Pageable pageable);

UserService.class;

@Service
public class UserService 

    @Autowired
    private UserRepository userRepository;

    public Page<UserDTO> findByFilter(Map<String, String> filters, Pageable pageable) 
        return userRepository.findAll(getFilterSpecification(filters), pageable);
    

    public Page<UserDTO> findByLogin(String filters, Pageable pageable) 
        return userRepository.findByLogin(filters.toLowerCase(), pageable);
    

    @Transactional
    public void create(UserDTO user) 
        userRepository.save(user);
    

    private Specification<UserDTO> getFilterSpecification(Map<String, String> filterValues) 
        return (Root<UserDTO> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> 
            Optional<Predicate> predicate = filterValues.entrySet().stream()
                    .filter(v -> v.getValue() != null && v.getValue().length() > 0)
                    .map(entry -> 
                        Path<?> path = root;
                        String key = entry.getKey();
                        if (entry.getKey().contains(".")) 
                            String[] splitKey = entry.getKey().split("\\.");
                            path = root.join(splitKey[0]);
                            key = splitKey[1];
                        
                        return builder.like(path.get(key).as(String.class), "%" + entry.getValue() + "%");
                    )
                    .collect(Collectors.reducing((a, b) -> builder.and(a, b)));
            return predicate.orElseGet(() -> alwaysTrue(builder));
        ;
    

    private Predicate alwaysTrue(CriteriaBuilder builder) 
        return builder.isTrue(builder.literal(true));
    


UserBean.class;

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class UserBean 

    @Autowired
    private UserService userService;
    private UserLazyDataModel users;

    @PostConstruct
    private void init() 
        this.users = new UserLazyDataModel(userService);
    

    public UserLazyDataModel getUsers() 
        return users;
    

UserLazyDataModel.class;

public class UserLazyDataModel extends LazyDataModel<UserDTO> implements SelectableDataModel<UserDTO> 

    private static final long serialVersionUID = -6123945723069023025L;
    private final transient UserService userService;
    private static final SortOrder DEFAULT_SORT_ORDER = SortOrder.ASCENDING;
    private static final String DEFAULT_SORT_FIELD = "id";
    private String columnName;
    private String filter;

    public UserLazyDataModel(UserService userService) 
        this.userService = userService;
    

    @Override
    public Object getRowKey(UserDTO user) 
        return user.getId();
    

    @Override
    public UserDTO getRowData(String rowKey) 
        Long rowId = Long.valueOf(rowKey);
        @SuppressWarnings("unchecked")
        List<UserDTO> users = (List<UserDTO>) super.getWrappedData();
        return users.stream().filter(user -> user.getId().equals(rowId)).findAny().orElse(null);
    

    @Override
    public List<UserDTO> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) 
        Sort sort = new Sort(getDirection(DEFAULT_SORT_ORDER), DEFAULT_SORT_FIELD);
        if (multiSortMeta != null) 
            List<Order> orders = multiSortMeta.stream()
                    .map(m -> new Order(getDirection(m.getSortOrder() != null ? m.getSortOrder() : DEFAULT_SORT_ORDER),
                            m.getSortField()))
                    .collect(Collectors.toList());
            sort = new Sort(orders);
        
        return filterAndSort(first, pageSize, filters, sort);
    

    @Override
    public List<UserDTO> load(int first, int pageSize, String sortField, SortOrder sortOrder,
            Map<String, Object> filters) 
        Sort sort = null;
        if (sortField != null) 
            sort = new Sort(getDirection(sortOrder != null ? sortOrder : DEFAULT_SORT_ORDER), sortField);
         else if (DEFAULT_SORT_FIELD != null) 
            sort = new Sort(getDirection(sortOrder != null ? sortOrder : DEFAULT_SORT_ORDER), DEFAULT_SORT_FIELD);
        
        return filterAndSort(first, pageSize, filters, sort);
    

    private List<UserDTO> filterAndSort(int first, int pageSize, Map<String, Object> filters, Sort sort) 
        Map<String, String> filtersMap = filters.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString()));

        if (!filters.isEmpty()) 
            filters.forEach((k, v) -> 
                columnName = new String(k.toString());
                filter = new String(v.toString());
            );
         else 
            columnName = new String("login");
            filter = new String("");
        

        Page<UserDTO> page = userService.findByFilter(filtersMap, new PageRequest(first / pageSize, pageSize, sort));
        Page<UserDTO> pageLogin = userService.findByLogin(filter, new PageRequest(first / pageSize, pageSize));
        this.setRowCount(((Number) pageLogin.getTotalElements()).intValue());
        this.setWrappedData(pageLogin.getContent());
        return pageLogin.getContent();
    

    private static Direction getDirection(SortOrder order) 
        switch (order) 
        case ASCENDING:
            return Direction.ASC;
        case DESCENDING:
            return Direction.DESC;
        case UNSORTED:
        default:
            return null;
        
    

index.xhtml;

<?xml version="1.0"?>
<ui:composition xmlns:f="http://java.sun.com/jsf/core"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:pe="http://primefaces.org/ui/extensions">
    <h:head>
    </h:head>
    <h:body styleClass="login">
        <f:view transient="true">
            <h:form id="form">
                <p:dataTable var="users" value="#userBean.users" paginator="true"
                    rows="10" sortMode="multiple"
                    paginatorTemplate="RowsPerPageDropdown FirstPageLink PreviousPageLink CurrentPageReport NextPageLink LastPageLink"
                    rowsPerPageTemplate="5,10,15" selectionMode="single" id="userTable"
                    lazy="true">
                    <p:column headerText="Id" sortBy="#users.id" filterBy="#users.id">
                        <h:outputText value="#users.id" />
                    </p:column>
                    <p:column headerText="Login" sortBy="#users.login"
                        filterBy="#users.login">
                        <h:outputText value="#users.login" />
                    </p:column>
                    <p:column headerText="Firstname" sortBy="#users.first_Name"
                        filterBy="#users.firstName">
                        <h:outputText value="#users.firstName" />
                    </p:column>
                    <p:column headerText="Lastname" sortBy="#users.lastName"
                        filterBy="#users.lastName">
                        <h:outputText value="#users.lastName" />
                    </p:column>
                    <p:column headerText="DayOfBirth" sortBy="#users.dayOfBirth"
                        filterBy="#users.dayOfBirth">
                        <h:outputText value="#users.dayOfBirth" />
                    </p:column>
                    <p:column headerText="Manager" sortBy="#users.manager.lastName"
                        filterBy="#users.manager.lastName">
                        <h:outputText value="#users.manager.lastName" />
                    </p:column>
                </p:dataTable>
            </h:form>
        </f:view>
    </h:body>
</ui:composition>

我还需要做什么?

【问题讨论】:

据我了解,您的存储库类使用 User 作为其实体而不是 UserDTO 如何将其转换为 DTO? @amRika 首先,检查它是否解决了您的beanNotFound异常问题 是的 .. 请在存储库类中使用 User 而不是 UserTO 尝试一次,让我知道它是否适合您。 它有效,谢谢!但它在我的 H2 控制台上创建了另一个名为 UserDTO 的表,这使得理解为我使用 DTO 的主要原因变得更加复杂。就我而言,DTO 应该使用与主实体对象相同的类。我错了吗? @amRika 【参考方案1】:
public interface UserRepository extends PagingAndSortingRepository<UserDTO, Long>, JpaSpecificationExecutor<UserDTO>, JpaRepository<UserDTO, Long>, Repository<UserDTO, Long>
@Query(value = "select * from user where login like :filters% order by login \n-- #pageable\n",
countQuery = "select count(*) from user where login like :filters%",
nativeQuery = true)
Page<UserDTO> findByLogin(@Param("filters") String filters, Pageable pageable);

通过查看 UserRepository,似乎没有创建 UserRepository 的 bean,因为您忘记使用 @Repository 注释对存储库进行注释。

您必须使用@Repository(为组件/类创建 Bean 的 Spring 原型)注释 UserRepository。因此,在创建 UserRepository 的 bean 之后,您将能够执行您的操作。

【讨论】:

但是在我创建 DTO 类之前,存储库类可以直接与实体类一起正常工作。【参考方案2】:

我在这段代码中没有看到任何注释。你应该写它们:)

public class UserDTO implements Serializable 

    private static final long serialVersionUID = -7860243025833384447L;

    private Long id;
    private String login;
    private String firstName;
    private String lastName;
    private Date dayOfBirth;
    private String city;
    private String district;

 //setters and getters


【讨论】:

以上是关于从实体到 DTO 的转换的主要内容,如果未能解决你的问题,请参考以下文章

在 TypeScript 和 NestS 中将类转换为类/对象(实体到 DTO)

将 DTO 转换为实体,反之亦然

Entity Class 到 DTO Class 的转换应该在哪一层处理?

具有业务对象、DTO 和实体/域对象的数据转换模式

我应该将实体(持久)对象转换为 DTO 对象吗?

是否有任何工具可以从实体生成 DTO?