英文:
Insert new entity: Spring Data JPA vs. Hibernate's EntityManager
问题
请查看下面的两个代码示例,我将在我的Spring Boot
项目中使用它们。它们都只是做同样的事情 - 将一个新对象添加到users
表中,该表由具有@Id
定义为用户名的User
实体表示,并在email
列上施加了unique
约束(还有其他一些列,但出于简洁起见,这里没有显示)。注意:我不能简单地使用CrudRepository
的save()
方法,因为如果现有记录和新对象都具有相同的username
值,它会将它们合并起来。相反,我需要插入一个新对象,并为重复数据持久性引发适当的异常。
我的问题是哪个选项应该优先考虑。通过EntityManager
,我不需要构造SQL语句。除此之外,有没有一种方法可能比另一种方法提供更多优势(尤其是在性能和资源消耗方面)?
此外,当我阅读关于在Spring Boot
中进行数据持久化的最新书籍和教程时,它们主要集中在Spring Data JPA
上。例如,《Spring in Action》的第5版几乎没有涉及Hibernate的EntityManager
。这是否意味着直接处理Hibernate
在现代项目中可能被视为一种“老派”做法,通常应该避免?
选项#1:Hibernate的EntityManager
@RestController
@RequestMapping(path = "/auth/register", produces = "application/json")
@Transactional
public class RegistrationController {
@PersistenceContext
EntityManager entityManager;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Map<String, String> registerNewUser(@RequestBody @Valid User newUser) {
try {
entityManager.persist(newUser);
entityManager.flush();
} catch (PersistenceException ex) {
// 解析异常以找出违反的约束是重复的用户名、电子邮件还是两者都有
String message = parseExceptionForConstraintNames(ex);
throw new ResponseStatusException(HttpStatus.CONFLICT, message);
}
return Collections.singletonMap("message", "Success...");
}
}
选项#2:CrudRepository的自定义@Query
@RestController
@RequestMapping(path = "/auth/register", produces = "application/json")
public class RegistrationController {
private final UsersRepository usersRepository;
@Autowired
public RegistrationController(UsersRepository usersRepository) {
this.usersRepository = usersRepository;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Map<String, String> registerNewUser(@RequestBody @Valid User newUser) {
try {
usersRepository.insert(newUser);
} catch (DataIntegrityViolationException ex) {
// 解析异常以找出违反的约束是重复的用户名、电子邮件还是两者都有
String message = parseExceptionForConstraintNames(ex);
throw new ResponseStatusException(HttpStatus.CONFLICT, message);
}
return Collections.singletonMap("message", "Success...");
}
}
public interface UsersRepository extends CrudRepository<User, String> {
@Modifying
@Transactional
@Query(nativeQuery = true, value = "INSERT INTO users (username, email) " +
"VALUES (:#{#user.username}, :#{#user.email})")
void insert(@Param("user") User newUser);
}
英文:
Please, look at the two code examples bellow which I'm going to use in my Spring Boot
project. They both do merely the same thing - add a new object into users
table, represented by User
entity with username
defined as @Id
and a unique
constraint imposed on email
column (there are some other columns as well, but they are not shown here for brevity). Note: I can't simply use save()
method from CrudRepository
, because it merges existing record with new object if they both have the same username
value. Instead, I need to insert a new object with appropriate exception thrown for duplicate data persistence.
My question is about which option should be given a favor. With EntityManager
, I don't need to construct SQL statement. Apart from that obvious observation, are there any advantages which one method may offer over the other (especially, in the matter of performance and resources consumption)?
Also, when I read latest books and tutorials about data persistence in Spring Boot
, they mainly focus on Spring Data JPA
. For example, the 5th edition of "Spring in Action" has no word about Hibernate's EntityMnager
. Does it mean that dealing with Hibernate
directly can be regarded as kind of "old school" and should generally be avoided in modern projects?
Option #1: Hibernate's EntityManager
@RestController
@RequestMapping(path = "/auth/register", produces = "application/json")
@Transactional
public class RegistrationController {
@PersistenceContext
EntityManager entityManager;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Map<String, String> registerNewUser(@RequestBody @Valid User newUser) {
try {
entityManager.persist(newUser);
entityManager.flush();
} catch (PersistenceException ex) {
// parse exception to find out which constraints have been
// broken - either it's duplicate username, email or both
String message = parseExceptionForConstraintNames(ex);
throw new ResponseStatusException(HttpStatus.CONFLICT, messsage);
}
return Collections.singletonMap("message", "Success...");
}
}
Option #2: custom @Query from CrudRepository
@RestController
@RequestMapping(path = "/auth/register", produces = "application/json")
public class RegistrationController {
private final UsersRepository usersRepository;
@Autowired
public RegistrationController(UsersRepository usersRepository) {
this.usersRepository = usersRepository;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Map<String, String> registerNewUser(@RequestBody @Valid User newUser) {
try {
usersRepository.insert(newUser);
} catch (DataIntegrityViolationException ex) {
// parse exception to find out which constraints have been
// broken - either it's duplicate username, email or both
String message = parseExceptionForConstraintNames(ex);
throw new ResponseStatusException(HttpStatus.CONFLICT, message);
}
return Collections.singletonMap("message", "Success...");
}
}
public interface UsersRepository extends CrudRepository<User, String> {
@Modifying
@Transactional
@Query(nativeQuery = true, value = "INSERT INTO users (username, email) " +
"VALUES (:#{#user.username}, :#{#user.email})")
void insert(@Param("user") User newUser);
}
答案1
得分: 2
参见此回答,了解使用 JPA 存储库与实体管理器。
最佳实践是不直接使用存储库。在 controller
和 repository
之间使用 Service 层,在这里您可以通过使用 findByUsername(String username);
来实现逻辑以检查记录在数据库中是否已存在,如果已存在则抛出异常,否则在数据库中进行 save()
操作。
英文:
See this answer for Using JPA repository vs Entity Manager.
Best practice is to not use Repository directly. use Service layer between controller
and repository
where you can implement the logic for duplicate entries by checking if the record already exist in DB using findByUsername(String username);
throw exception if it already exist else save()
the object in DB
答案2
得分: 1
根据给定的要求,实体中的“username”字段从未符合“@Id”的条件。
为什么不能添加一个显式的id字段,并为id字段添加一个序列生成器,然后只将“username”标记为“unique”约束呢?
英文:
With the given requirements, the username
filed in the entity never qualifies for the @Id
.
Why can't u add an explicit id field with some sequence generator for the id filed and just keep the username
marked with unique
constraint only.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论