【发布时间】:2016-12-06 17:42:28
【问题描述】:
我的数据库中有 2 个实体,具有一对一的方向映射: 用户和密码重置令牌。这背后的想法是在每次用户请求密码重置时创建新令牌并仅存储最新的令牌。
以下是我的实体:
@Entity
@Table(name = "USERS")
@Getter @Setter
public class User implements Serializable {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "usersSeq")
@SequenceGenerator(name = "usersSeq", sequenceName = "SEQ_USERS", allocationSize = 1)
private long id;
@Column(name = "NAME")
private String name;
@Column(name = "PASSWORD")
private String password;
@Column(name = "EMAIL")
private String email;
@Column(name = "ROLE")
private Integer role;
}
///...
@Entity
@Table(name = "PASSWORD_RESET_TOKENS")
@Getter
@Setter
public class PasswordResetToken implements Serializable {
private static final int EXPIRATION = 24;
@Column(name = "TOKEN")
private String token;
@Id
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(nullable = false, name = "user_id")
private User user;
@Column(name = "EXPIRY_DATE")
private Instant expiryDate;
public PasswordResetToken() {
}
public void setExpiryDate(ZonedDateTime expiryDate) {
this.expiryDate = expiryDate.plus(EXPIRATION, ChronoUnit.HOURS).toInstant();
}
}
另外,我为他们创建了 DTO,以便在我的应用程序中传递它们。 代码sn-ps:
@Getter @Setter
public class PasswordResetTokenModel {
private String token;
private ZonedDateTime expiryDate;
private UserModel user;
}
UserModel 也用于 Spring Security
@Getter
@Setter
public class UserModel extends User {
public UserModel(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
private long id;
private String name;
public String getEmail() {
return this.getUsername();
}
}
对于人口,我创建了 2 个人口:
@Component
public class UserPopulatorImpl implements UserPopulator {
@Autowired
UserDetailsService userDetailsService;
@Override
public UserModel populateToDTO(User user) {
UserModel userModel = new UserModel(user.getEmail(), user.getPassword(), userDetailsService.getAuthorities(user.getRole()));
userModel.setId(user.getId());
return userModel;
}
@Override
public User populateToDAO(UserModel userModel) {
User user = new User();
user.setEmail(userModel.getEmail());
user.setName(userModel.getName());
user.setPassword(userModel.getPassword());
//TODO: change it!
user.setRole(1);
return user;
}
}
//...
@Component
public class PasswordResetTokenPopulatorImpl implements PasswordResetTokenPopulator {
@Autowired
UserPopulator userPopulator;
@Override
public PasswordResetTokenModel populateToDTO(PasswordResetToken passwordResetToken) {
PasswordResetTokenModel passwordResetTokenModel = new PasswordResetTokenModel();
passwordResetTokenModel.setUser(userPopulator.populateToDTO(passwordResetToken.getUser()));
passwordResetTokenModel.setToken(passwordResetToken.getToken());
passwordResetTokenModel.setExpiryDate(ZonedDateTime.ofInstant(passwordResetToken.getExpiryDate(), ZoneId.systemDefault()));
return passwordResetTokenModel;
}
@Override
public PasswordResetToken populateToDAO(PasswordResetTokenModel passwordResetTokenModel) {
PasswordResetToken passwordResetToken = new PasswordResetToken();
passwordResetToken.setExpiryDate(passwordResetTokenModel.getExpiryDate());
passwordResetToken.setUser(userPopulator.populateToDAO(passwordResetTokenModel.getUser()));
passwordResetToken.setToken(passwordResetTokenModel.getToken());
return passwordResetToken;
}
}
我正在使用
保存对象sessionFactory.getCurrentSession().saveOrUpdate(token);
当我使用此代码时,出现以下异常
object references an unsaved transient instance - save the transient instance before flushing: com.demo.megaevents.entities.User
此代码目前存在 2 个问题:
- 似乎我的 OneToOne 映射中的 Cascade.ALL 不起作用。如果 我在 Token 类中创建了单独的主键,一切正常 正如预期的那样,但将每个创建的令牌存储在数据库中(更像 OneToMany 关系),但是我想避免它,因为我需要存储 我的数据库中每个用户只有一个令牌
- 我不喜欢在填充器中使用 new,因为它会强制休眠在刷新会话时创建新对象。但是,我也不想再做一次选择来从数据库中获取这些数据,因为在之前提到的填充器我已经做了这个查询来获取它,我认为这是一个开销。
另外,我真的很想拥有 DTO,我不想删除 DTO 层。
所以,我的问题:
- 在 DTO 和实体之间处理填充的正确方法是什么?
- 我的解决方案是否还有其他改进(可能是架构方面的)?
非常感谢。
【问题讨论】:
-
可能我没有正确理解您的应用程序,但是如果您在 Token 类中添加单独的主键,删除
@JoinColumn(nullable = false, name = "user_id")并在您的 User 类中添加@OneToOne (mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL) PasswordResetToken token;,也许您会达到预期的效果 -
@lenach87,我不希望用户知道令牌,但其他方式。
标签: java spring hibernate dto one-to-one