通过与ID字段不同的字段映射对象的相关实体

huangapple go评论71阅读模式
英文:

Mapping object's related entities by other field than ID

问题

我正在使用以下实体:

@Entity
@Table(name = "books")
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @ManyToOne
    private User user;
    private UUID uuid = UUID.randomUUID();
}

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String email;
    private String firstName;
    private String lastName;
    private String password;
    @OneToMany(mappedBy = "user")
    private List<Book> books;
    private UUID uuid = UUID.randomUUID();
}

以及以下DTO:

public class NewBookRequest {
    private UUID userUuid;
}

和转换器:

@Component
public class NewBookRequestToEntityConverter implements Converter<NewBookRequest, Book> {

    private final ModelMapper modelMapper;

    public NewBookRequestToEntityConverter(final ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
    }

    @Override
    public Book convert(@NotNull final NewBookRequest source) {
        return modelMapper.map(source, Book.class);
    }
}

并且这是部分服务代码:

public void addBook(final NewBookRequest newBookRequest) {
    final Book newBook = newBookRequestToEntityConverter.convert(newBookRequest);
    bookRepository.save(newBook);
}

当我尝试保存转换后的Book实体时,我会收到ConstraintViolationException异常,因为newBook.user.id为null。但是,newBook.user.uuid被正确赋值。有没有自动按其uuid映射newBook的user的方法?或者唯一的解决方案是做像这样的事情:

添加新方法到转换器:

@Component
public class NewBookRequestToEntityConverter implements Converter<NewBookRequest, Book> {

    private final ModelMapper modelMapper;

    public NewBookRequestToEntityConverter(final ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
    }

    @Override
    public Book convert(@NotNull final NewBookRequest source) {
        return modelMapper.map(source, Book.class);
    }

    public Book convert(@NotNull final NewBookRequest source, final User user) {
        Book target = modelMapper.map(source, Book.class);
        target.setUser(user);
        return target;
    }
}

并修改服务代码:

public void addBook(final NewBookRequest newBookRequest) {
    final User user = userRepository.getUserByUuid(newBookRequest.getUserUuid());
    final Book newBook = newBookRequestToEntityConverter.convert(newBookRequest, user);
    bookRepository.save(newBook);
}

?谢谢任何帮助!

英文:

I am using the following entities:

@Entity
@Table(name = &quot;books&quot;)
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @ManyToOne
    private User user;
    private UUID uuid = UUID.randomUUID();
}

and

@Entity
@Table(name = &quot;users&quot;)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String email;
    private String firstName;
    private String lastName;
    private String password;
    @OneToMany(mappedBy = &quot;user&quot;)
    private List&lt;Book&gt; books;
    private UUID uuid = UUID.randomUUID();
}

and the following DTO:

public class NewBookRequest {
    private UUID userUuid;
}

and the converter:

@Component
public class NewBookRequestToEntityConverter implements Converter&lt;NewBookRequest, Book&gt; {

    private final ModelMapper modelMapper;

    public NewBookRequestToEntityConverter(final ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
    }

    @Override
    public Book convert(@NotNull final NewBookRequest source) {
        return modelMapper.map(source, Book.class);
    }
}

and there is part of service code:

public void addBook(final NewBookRequest newBookRequest) {
    final Book newBook = newBookRequestToEntityConverter.convert(newBookRequest);
    bookRepository.save(newBook);
}

When I try to save the converted Book entity I get the ConstraintViolationException exception, as newBook.user.id is null. However, newBook.user.uuid is correctly assigned. Is there any way to automatically map newBook's user by its uuid? Or the only solution is to do something like this:

add new method to converter:

@Component
public class NewBookRequestToEntityConverter implements Converter&lt;NewBookRequest, Book&gt; {

    private final ModelMapper modelMapper;

    public NewBookRequestToEntityConverter(final ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
    }

    @Override
    public Book convert(@NotNull final NewBookRequest source) {
        return modelMapper.map(source, Book.class);
    }

    public Book convert(@NotNull final NewBookRequest source, final User user) {
        Book target = modelMapper.map(source, Book.class);
        target.setUser(user);
        return target;
    }
}

and modify service's code:

public void addBook(final NewBookRequest newBookRequest) {
    final User user = userRepository.getUserByUuid(newBookRequest.getUserUuid());
    final Book newBook = newBookRequestToEntityConverter.convert(newBookRequest, user);
    bookRepository.save(newBook);
}

? Thanks for any help!

答案1

得分: 1

在"Book"或"User"实体中,您可以使用UUID作为主键。您尝试过这样做吗?

@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
    name = "UUID",
    strategy = "org.hibernate.id.UUIDGenerator",
)
@Column(name = "id", updatable = false, nullable = false)
private UUID id;

然而,在某些数据库中,如果存在高负载,基于UUID的索引可能会导致性能问题。

英文:

In Book or User entity you could use UUID as primary key. Did you try that?

@Id
@GeneratedValue(generator = &quot;UUID&quot;)
@GenericGenerator(
	name = &quot;UUID&quot;,
	strategy = &quot;org.hibernate.id.UUIDGenerator&quot;,
)
@Column(name = &quot;id&quot;, updatable = false, nullable = false)
private UUID id;

However on some databases there can be performance issue when using indexes based on UUID if there is high load.

答案2

得分: 1

我认为问题在于您的转换器未初始化 User 的主键。
您可以更改 User 类中 id 字段的类型,具体原因请参考 https://stackoverflow.com/a/64141178/14225495,或者在 Book 类中的 user 字段上添加注释:

@JoinColumn(name = "user_uuid", referencedColumnName = "uuid")

在这种情况下,您还应在 books 表中添加 user_uuid 列,确保 users.uuid 列是唯一的且不可为空,以创建外键。

英文:

I think problem is your converter does not initialize the primary key of User.
You may either change the type of id field in the User class due to answer https://stackoverflow.com/a/64141178/14225495, or add the annotation

@JoinColumn(name = &quot;user_uuid&quot;, referencedColumnName = &quot;uuid&quot;)

to the user field in the Book class. In this case you should also add column &quot;user_uuid&quot; in the books table, make sure the users.uuid column is unique and not nullable to create foreign key.

huangapple
  • 本文由 发表于 2020年9月30日 23:12:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/64140609.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定