我使用Java和Spring Data JPA。到目前为止,我一直使用Long作为我的ID。但事实证明,我需要它在我的DTO。因此,出于安全原因,我需要UUID或GUID。所以我读了一点关于它的信息,并从Long更改为UUID。期望它是顺利的,没有太多的问题切换,但我们在这里。
所以我有一个Entity,Repository,DTO,Mapper(由mapstruct生成)。我列出所有这些的原因是,如果你对我的代码结构有建议的话。
我有一个测试,那就是测试存储库。
这就是考验
//I'm using this on top of my test class
@DataJpaTest
public class RoleRepositoryTests {
@Test
public void saveToDBShouldThrowExceptionTest(){
//Given
Role userRole = new Role();
try {
//When
roleRepository.save(userRole);
//Then
fail("Expected ConstraintViolationException was not thrown");
} catch (ConstraintViolationException e) {
//The above will throw two exceptions cuz
//both @NotBlank and @NotNull will throw an exception.
//it throws them in random order every time
assertThat(e.getConstraintViolations().size()).isEqualTo(2);
return;
}
fail("Different Exception was thrown");
}
}
实体
@Entity
@Getter @Setter
@ToString
@NoArgsConstructor
@Table(name = "Roles")
public class Role {
@Id @GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@NotNull
@NotBlank
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Role role = (Role) o;
return id.equals(role.id) && name.equals(role.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
仓库
@Repository
public interface RoleRepository extends CrudRepository<Role, UUID> {
Optional<Role> findByName(String name);
}
在测试中,我期望roleRepository.save(userRole)抛出ConstraintViolationException。因为某些原因它不这样做。自从我从Long切换到UUID后,我决定回去把东西改回Long,看看是否有效(当然,我改变了存储库、生成类型和字段类型)。令人困惑的是它起作用了。测试抛出了异常,因此验证工作正常。我不知道是什么引起的。
@DataJpaTest默认在内存db中创建一个h2。我没有改变这一点,这就是我正在测试的。表、约束和非空值将按预期创建。
create table Roles (
id uuid not null,
name varchar(255) not null,
primary key (id)
)
它设法以某种方式将一个为null的名称保存到上面的表中,尽管not null在那里。
我试着对我的真实的数据库(ms sql)Roles Table进行测试。没有抛出异常,但也没有将任何内容保存到数据库。使用这个注解@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)来测试真实的数据库。
它每次都会生成不同的UUID。(Idk如果它应该这样做或不)
我所拥有的所有实体都会发生这种情况所以我不知道我错过了什么。
我试过用maven做清洁建筑。我已经尝试将@Validated添加到我的存储库和实体中。我已经尝试重新启动我的IDE(IntelliJ)我会尝试重新启动我的电脑后,我张贴这一点。没成功尝试了更多的东西,得到了一个 unrecognized id type:uuid -〉java.util.UUID。通过执行@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private UUID id;
获得
找到了这个post。正在阅读它。
2条答案
按热度按时间af7jpaap1#
找到解决办法了!不允许数据库生成GUID/UUID。你在代码中生成它。
另外,不要忘记将存储库更新为String!
而且它仍然不会直接抛出ConstraintViolationException。
这是有效的,但是当你试图保存违反验证的东西时,它会抛出一个org.springframework.transaction.TransactionSystemException:无法提交由导致的JPA事务导致原因:jakarta.validation.ConstraintViolationException:类验证失败。我希望它直接抛出ConstraintViolationException,但我们在这里。
仍然没有使用h2进行验证。所以基本上我什么都没解决。
旧半溶液:
有点找到了解决方案,但这并不是我所期望的。
所以导致这个问题的原因是db(h2或mssql)和spring数据jpa(hibernate)不能就该怎么做达成一致。我最接近的解决办法是
或
使用Hibernate 6+
这是可行的,但是当你试图保存一些违反验证的东西时,它会抛出一个
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction
,这是由Caused by: jakarta.validation.ConstraintViolationException: Validation failed for classes
引起的。我希望它直接抛出ConstraintViolationException
,但我们在这里。这也不适用于H2。由于某种原因,h2不尊重验证。它直接忽略了表和实体验证。
解决方案,应该已经工作了开箱即用,但它不会与mssql也不是h2是
H2很好地(虽然抛出了一些异常,但没有创建表)告诉我它不支持它。
MSSQL告诉我它不知道uuid是什么。
org.springframework.orm.jpa.JpaSystemException: unrecognized id type : uuid -> java.util.UUID
。尝试将GenericGenerator策略也更改为guid。从这里得到的想法。(guid -在MS SQL Server和MySQL上使用数据库生成的GUID字符串。)如果你设法让它与.IDENTITY一起工作,我认为你需要这个
(newsequentialid())
或(newId())
作为mssql的默认值。我相信有一个更好的解决方案,所以我不会把这个标记为答案。更好的解决方案就在^上面。
pbossiut2#
您还可以指定UUID对象在数据库中Map到的类型。也许这可以帮助你与两个数据库。
在你的应用程序.yml文件中添加例如:
spring.jpa.properties.hibernate.type.preferred_uuid_jdbc_type: CHAR
这将把UUIDMap到MSSQL中的CHAR列。您甚至可以通过spring配置文件切换应用程序上下文来更改测试上下文的Map。
参见:https://docs.jboss.org/hibernate/orm/6.2/migration-guide/migration-guide.html