LOADING

Follow me

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】
六月 22, 2017|DockerPaaS

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】

本期的译见,将带您继续前行:详细介绍一个完整的基于 JPA 的用户存储库实现,一个 JPA 的支撑模型和一些测试用例。

Misha&Boring 译  道客船长

译见|构建用户管理微服务(三):实现和测试存储库

在上期的《译见|构建用户管理微服务(二):实现领域模型》中,有着相当数量的涉及到实现领域模型的编码,它们构成了用户注册过程中所需要的全部逻辑。在第三部分中,作者将带你继续前行:详细介绍一个完整的基于 JPA 的用户存储库实现,一个 JPA 的支撑模型和一些测试用例。

 

使用 XML 来映射简单的 JAVA 对象

仅看到用户存储库,也许你就能想到在对它添加基于 JPA 的实现时会遇到什么困难。

1
2
3
4
5
6
7
8
9
public interface UserRepository {  
 
void delete(Long userId) throws NoSuchUserException;  
Optional<User> findById(Long id);  
Optional<User> findByEmail(String email);  
Optional<User> findByScreenName(String screenName);  
User save(User user);
 
}

 

但是, 正如我在第一部分提到的, 我们将使用 DDD (域驱动设计), 因此, 在模型中就不能使用特定框架的依赖关系云 (包括 JPA 的注解) ,剩下的唯一可行性方法是用 XML 进行映射。如果我没有记错的话,自2010年以来,我再也没有接触过任何一个 orm.xml 的文件 , 这也就是我为什么开始怀念它的原因。

接下来我们看看XML文件中User的映射情况,以下是 user-orm.xml 的部分摘录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<entity class=“com.springuni.auth.domain.model.user.User” cacheable=“true” metadatacomplete=“true”>
<table name=“user_”/>
<namedquery name=“findByIdQuery”>  <query>    
<![CDATA[
     select u from User u      
     where u.id = :userId      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<namedquery name=“findByEmailQuery”>  <query>    
<![CDATA[      
     select u from User u      
     where u.contactData.email = :email      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<namedquery name=“findByScreenNameQuery”>  <query>    
<![CDATA[      
     select u from User u      
     where u.screenName = :screenName      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<entitylisteners>  
<entitylistener class=“com.springuni.commons.jpa.IdentityGeneratorListener”/>
</entitylisteners>
<attributes>  
<id name=“id”/>  
<basic name=“timezone”>    
<enumerated>STRING</enumerated>  
</basic>  <basic name=“locale”/>  
<basic name=“confirmed”/>  
<basic name=“locked”/>  
<basic name=“deleted”/>  
<onetomany name=“confirmationTokens” fetch=“LAZY” mappedby=“owner” orphanremoval=“true”>    
<cascade>      
<cascadepersist/>      
  <cascademerge/>    
</cascade>
</onetomany>  
  <elementcollection name=“authorities”>    
   <collectiontable name=“authority”>      
   <joincolumn name=“user_id”/>    
</collectiontable>  
</elementcollection>  
<embedded name=“auditData”/>  
<embedded name=“contactData”/>  
<embedded name=“password”/>  
<! Do not map email directly through its getter/setter >  <transient name=“email”/>
 
</attributes>
 
</entity>

 

域驱动设计是一种持久化无关的方法,因此坚持设计一个没有具体目标数据结构的模型可能很有挑战性。当然, 它也存在优势, 即可对现实世界中的问题直接进行建模, 而不存在只能以某种方式使用某种技术栈之类的副作用。

1
2
3
4
5
6
7
8
public class User implements Entity<Long, User> {  
 
private Long id;  
private String screenName;  ...  
 
private Set<String> authorities = new LinkedHashSet<>();
 
}

一般来说,一组简单的字符串或枚举值就能对用户的权限(或特权)进行建模了。

使用像 MongoDB 这样的文档数据库能够轻松自然地维护这个模型,如下所示。(顺便一提, 我还计划在本系列的后续内容中添加一个基于 Mongo 的存储库实现)

1
2
3
4
5
6
7
{   “id”:123456789,  
“screenName”:“test”,   ...  
“authorities”:[      
             “USER”,      
             “ADMIN”  
]
}

然而, 在关系模型中, 权限的概念必须作为用户的子关系进行处理。但是在现实世界中, 这仅仅只是一套权限规则。我们需要如何弥合这样的差距呢?

在 JPA 2.0 中可以引入 ElementCollection 来进行操作,它的用法类似于 OneToMany。在这种情况下, 已经配置好的 JPA 提供的程序 (Hibernate) 将自动生成必要的子关系。

create table authority (  user_id bigint not null,  authorities varchar(255) )

alter table authority  add constraint FKoia3663r5o44m6knaplucgsxn  foreign key (user_id) references user_

项目中的新模块

我一直在讨论的 springuni-auth-user-jpa 包含了一个完整的基于 JPA 的 UserRepository 实现。其目标是, 每个模块都应该只拥有那些对它们的操作来说绝对必要的依赖关系,而这些关系只需要依赖 JPA API 便可以实现。

springuni-commons-jpa 是一个支撑模块, 它能够使用预先配置好的 HikariCP 和 Hibernate 的组合作为实体管理器, 而不必关心其他细节。 它的特色是 AbstractJpaConfiguration, 类似于 Spring Boot 的 HibernateJpaAutoConfiguration。

然而我没有使用后者的原因是 Spring Boot 的自动配置需要一定的初始化。因为谷歌应用引擎标准环境是我的目标平台之一,因此能否快速地启动是至关重要的。

单元测试存储库

虽然有人可能会说, 对于存储库没必要进行过多的测试, 尤其是在使用 Spring Data 的 存储库接口的时候。但是我认为测试代码可以避免运行时存在的一些问题,例如错误的实体映射或错误的 JPQL 查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@RunWith(SpringJUnit4ClassRunner)
@ContextConfiguration(classes = [UserJpaTestConfiguration])
@Transactional
@Rollbackclass UserJpaRepositoryTest {
 
  @Autowired
  UserRepository userRepository
 
  User user
 
  @Before  void before() {
    user = new User(1, “test”, “test@springuni.com”)
    user.addConfirmationToken(ConfirmationTokenType.EMAIL, 10)
    userRepository.save(user)
  }
 
  ...
 
  @Test  void testFindById() {
    Optional<User> userOptional = userRepository.findById(user.id)
    assertTrue(userOptional.isPresent())
  }
 
  ...
 
}

这个测试用例启动了一个具有嵌入式 H2 数据库的实体管理器。H2 非常适合于测试, 因为它支持许多众所周知的数据库 (如 MySQL) 的兼容模式,可以模拟你的真实数据库。

下期预告:构建用户管理微服务(四):实现 REST 控制器

原文链接:https://www.springuni.com/user-management-microservice-part-3

no comments
Share

发表评论

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】
六月 22, 2017|DockerPaaS

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】

本期的译见,将带您继续前行:详细介绍一个完整的基于 JPA 的用户存储库实现,一个 JPA 的支撑模型和一些测试用例。

Misha&Boring 译  道客船长

译见|构建用户管理微服务(三):实现和测试存储库

在上期的《译见|构建用户管理微服务(二):实现领域模型》中,有着相当数量的涉及到实现领域模型的编码,它们构成了用户注册过程中所需要的全部逻辑。在第三部分中,作者将带你继续前行:详细介绍一个完整的基于 JPA 的用户存储库实现,一个 JPA 的支撑模型和一些测试用例。

 

使用 XML 来映射简单的 JAVA 对象

仅看到用户存储库,也许你就能想到在对它添加基于 JPA 的实现时会遇到什么困难。

1
2
3
4
5
6
7
8
9
public interface UserRepository {  
 
void delete(Long userId) throws NoSuchUserException;  
Optional<User> findById(Long id);  
Optional<User> findByEmail(String email);  
Optional<User> findByScreenName(String screenName);  
User save(User user);
 
}

 

但是, 正如我在第一部分提到的, 我们将使用 DDD (域驱动设计), 因此, 在模型中就不能使用特定框架的依赖关系云 (包括 JPA 的注解) ,剩下的唯一可行性方法是用 XML 进行映射。如果我没有记错的话,自2010年以来,我再也没有接触过任何一个 orm.xml 的文件 , 这也就是我为什么开始怀念它的原因。

接下来我们看看XML文件中User的映射情况,以下是 user-orm.xml 的部分摘录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<entity class=“com.springuni.auth.domain.model.user.User” cacheable=“true” metadatacomplete=“true”>
<table name=“user_”/>
<namedquery name=“findByIdQuery”>  <query>    
<![CDATA[
     select u from User u      
     where u.id = :userId      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<namedquery name=“findByEmailQuery”>  <query>    
<![CDATA[      
     select u from User u      
     where u.contactData.email = :email      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<namedquery name=“findByScreenNameQuery”>  <query>    
<![CDATA[      
     select u from User u      
     where u.screenName = :screenName      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<entitylisteners>  
<entitylistener class=“com.springuni.commons.jpa.IdentityGeneratorListener”/>
</entitylisteners>
<attributes>  
<id name=“id”/>  
<basic name=“timezone”>    
<enumerated>STRING</enumerated>  
</basic>  <basic name=“locale”/>  
<basic name=“confirmed”/>  
<basic name=“locked”/>  
<basic name=“deleted”/>  
<onetomany name=“confirmationTokens” fetch=“LAZY” mappedby=“owner” orphanremoval=“true”>    
<cascade>      
<cascadepersist/>      
  <cascademerge/>    
</cascade>
</onetomany>  
  <elementcollection name=“authorities”>    
   <collectiontable name=“authority”>      
   <joincolumn name=“user_id”/>    
</collectiontable>  
</elementcollection>  
<embedded name=“auditData”/>  
<embedded name=“contactData”/>  
<embedded name=“password”/>  
<! Do not map email directly through its getter/setter >  <transient name=“email”/>
 
</attributes>
 
</entity>

 

域驱动设计是一种持久化无关的方法,因此坚持设计一个没有具体目标数据结构的模型可能很有挑战性。当然, 它也存在优势, 即可对现实世界中的问题直接进行建模, 而不存在只能以某种方式使用某种技术栈之类的副作用。

1
2
3
4
5
6
7
8
public class User implements Entity<Long, User> {  
 
private Long id;  
private String screenName;  ...  
 
private Set<String> authorities = new LinkedHashSet<>();
 
}

一般来说,一组简单的字符串或枚举值就能对用户的权限(或特权)进行建模了。

使用像 MongoDB 这样的文档数据库能够轻松自然地维护这个模型,如下所示。(顺便一提, 我还计划在本系列的后续内容中添加一个基于 Mongo 的存储库实现)

1
2
3
4
5
6
7
{   “id”:123456789,  
“screenName”:“test”,   ...  
“authorities”:[      
             “USER”,      
             “ADMIN”  
]
}

然而, 在关系模型中, 权限的概念必须作为用户的子关系进行处理。但是在现实世界中, 这仅仅只是一套权限规则。我们需要如何弥合这样的差距呢?

在 JPA 2.0 中可以引入 ElementCollection 来进行操作,它的用法类似于 OneToMany。在这种情况下, 已经配置好的 JPA 提供的程序 (Hibernate) 将自动生成必要的子关系。

create table authority (  user_id bigint not null,  authorities varchar(255) )

alter table authority  add constraint FKoia3663r5o44m6knaplucgsxn  foreign key (user_id) references user_

项目中的新模块

我一直在讨论的 springuni-auth-user-jpa 包含了一个完整的基于 JPA 的 UserRepository 实现。其目标是, 每个模块都应该只拥有那些对它们的操作来说绝对必要的依赖关系,而这些关系只需要依赖 JPA API 便可以实现。

springuni-commons-jpa 是一个支撑模块, 它能够使用预先配置好的 HikariCP 和 Hibernate 的组合作为实体管理器, 而不必关心其他细节。 它的特色是 AbstractJpaConfiguration, 类似于 Spring Boot 的 HibernateJpaAutoConfiguration。

然而我没有使用后者的原因是 Spring Boot 的自动配置需要一定的初始化。因为谷歌应用引擎标准环境是我的目标平台之一,因此能否快速地启动是至关重要的。

单元测试存储库

虽然有人可能会说, 对于存储库没必要进行过多的测试, 尤其是在使用 Spring Data 的 存储库接口的时候。但是我认为测试代码可以避免运行时存在的一些问题,例如错误的实体映射或错误的 JPQL 查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@RunWith(SpringJUnit4ClassRunner)
@ContextConfiguration(classes = [UserJpaTestConfiguration])
@Transactional
@Rollbackclass UserJpaRepositoryTest {
 
  @Autowired
  UserRepository userRepository
 
  User user
 
  @Before  void before() {
    user = new User(1, “test”, “test@springuni.com”)
    user.addConfirmationToken(ConfirmationTokenType.EMAIL, 10)
    userRepository.save(user)
  }
 
  ...
 
  @Test  void testFindById() {
    Optional<User> userOptional = userRepository.findById(user.id)
    assertTrue(userOptional.isPresent())
  }
 
  ...
 
}

这个测试用例启动了一个具有嵌入式 H2 数据库的实体管理器。H2 非常适合于测试, 因为它支持许多众所周知的数据库 (如 MySQL) 的兼容模式,可以模拟你的真实数据库。

下期预告:构建用户管理微服务(四):实现 REST 控制器

原文链接:https://www.springuni.com/user-management-microservice-part-3

no comments
Share

发表评论

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】
六月 22, 2017|DockerPaaS

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】

译见|构建用户管理微服务(三):实现和测试存储库【zoues.com】

本期的译见,将带您继续前行:详细介绍一个完整的基于 JPA 的用户存储库实现,一个 JPA 的支撑模型和一些测试用例。

Misha&Boring 译  道客船长

译见|构建用户管理微服务(三):实现和测试存储库

在上期的《译见|构建用户管理微服务(二):实现领域模型》中,有着相当数量的涉及到实现领域模型的编码,它们构成了用户注册过程中所需要的全部逻辑。在第三部分中,作者将带你继续前行:详细介绍一个完整的基于 JPA 的用户存储库实现,一个 JPA 的支撑模型和一些测试用例。

 

使用 XML 来映射简单的 JAVA 对象

仅看到用户存储库,也许你就能想到在对它添加基于 JPA 的实现时会遇到什么困难。

1
2
3
4
5
6
7
8
9
public interface UserRepository {  
 
void delete(Long userId) throws NoSuchUserException;  
Optional<User> findById(Long id);  
Optional<User> findByEmail(String email);  
Optional<User> findByScreenName(String screenName);  
User save(User user);
 
}

 

但是, 正如我在第一部分提到的, 我们将使用 DDD (域驱动设计), 因此, 在模型中就不能使用特定框架的依赖关系云 (包括 JPA 的注解) ,剩下的唯一可行性方法是用 XML 进行映射。如果我没有记错的话,自2010年以来,我再也没有接触过任何一个 orm.xml 的文件 , 这也就是我为什么开始怀念它的原因。

接下来我们看看XML文件中User的映射情况,以下是 user-orm.xml 的部分摘录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<entity class=“com.springuni.auth.domain.model.user.User” cacheable=“true” metadatacomplete=“true”>
<table name=“user_”/>
<namedquery name=“findByIdQuery”>  <query>    
<![CDATA[
     select u from User u      
     where u.id = :userId      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<namedquery name=“findByEmailQuery”>  <query>    
<![CDATA[      
     select u from User u      
     where u.contactData.email = :email      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<namedquery name=“findByScreenNameQuery”>  <query>    
<![CDATA[      
     select u from User u      
     where u.screenName = :screenName      
     and u.deleted = false    
   ]]>  </query>
</namedquery>
<entitylisteners>  
<entitylistener class=“com.springuni.commons.jpa.IdentityGeneratorListener”/>
</entitylisteners>
<attributes>  
<id name=“id”/>  
<basic name=“timezone”>    
<enumerated>STRING</enumerated>  
</basic>  <basic name=“locale”/>  
<basic name=“confirmed”/>  
<basic name=“locked”/>  
<basic name=“deleted”/>  
<onetomany name=“confirmationTokens” fetch=“LAZY” mappedby=“owner” orphanremoval=“true”>    
<cascade>      
<cascadepersist/>      
  <cascademerge/>    
</cascade>
</onetomany>  
  <elementcollection name=“authorities”>    
   <collectiontable name=“authority”>      
   <joincolumn name=“user_id”/>    
</collectiontable>  
</elementcollection>  
<embedded name=“auditData”/>  
<embedded name=“contactData”/>  
<embedded name=“password”/>  
<! Do not map email directly through its getter/setter >  <transient name=“email”/>
 
</attributes>
 
</entity>

 

域驱动设计是一种持久化无关的方法,因此坚持设计一个没有具体目标数据结构的模型可能很有挑战性。当然, 它也存在优势, 即可对现实世界中的问题直接进行建模, 而不存在只能以某种方式使用某种技术栈之类的副作用。

1
2
3
4
5
6
7
8
public class User implements Entity<Long, User> {  
 
private Long id;  
private String screenName;  ...  
 
private Set<String> authorities = new LinkedHashSet<>();
 
}

一般来说,一组简单的字符串或枚举值就能对用户的权限(或特权)进行建模了。

使用像 MongoDB 这样的文档数据库能够轻松自然地维护这个模型,如下所示。(顺便一提, 我还计划在本系列的后续内容中添加一个基于 Mongo 的存储库实现)

1
2
3
4
5
6
7
{   “id”:123456789,  
“screenName”:“test”,   ...  
“authorities”:[      
             “USER”,      
             “ADMIN”  
]
}

然而, 在关系模型中, 权限的概念必须作为用户的子关系进行处理。但是在现实世界中, 这仅仅只是一套权限规则。我们需要如何弥合这样的差距呢?

在 JPA 2.0 中可以引入 ElementCollection 来进行操作,它的用法类似于 OneToMany。在这种情况下, 已经配置好的 JPA 提供的程序 (Hibernate) 将自动生成必要的子关系。

create table authority (  user_id bigint not null,  authorities varchar(255) )

alter table authority  add constraint FKoia3663r5o44m6knaplucgsxn  foreign key (user_id) references user_

项目中的新模块

我一直在讨论的 springuni-auth-user-jpa 包含了一个完整的基于 JPA 的 UserRepository 实现。其目标是, 每个模块都应该只拥有那些对它们的操作来说绝对必要的依赖关系,而这些关系只需要依赖 JPA API 便可以实现。

springuni-commons-jpa 是一个支撑模块, 它能够使用预先配置好的 HikariCP 和 Hibernate 的组合作为实体管理器, 而不必关心其他细节。 它的特色是 AbstractJpaConfiguration, 类似于 Spring Boot 的 HibernateJpaAutoConfiguration。

然而我没有使用后者的原因是 Spring Boot 的自动配置需要一定的初始化。因为谷歌应用引擎标准环境是我的目标平台之一,因此能否快速地启动是至关重要的。

单元测试存储库

虽然有人可能会说, 对于存储库没必要进行过多的测试, 尤其是在使用 Spring Data 的 存储库接口的时候。但是我认为测试代码可以避免运行时存在的一些问题,例如错误的实体映射或错误的 JPQL 查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@RunWith(SpringJUnit4ClassRunner)
@ContextConfiguration(classes = [UserJpaTestConfiguration])
@Transactional
@Rollbackclass UserJpaRepositoryTest {
 
  @Autowired
  UserRepository userRepository
 
  User user
 
  @Before  void before() {
    user = new User(1, “test”, “test@springuni.com”)
    user.addConfirmationToken(ConfirmationTokenType.EMAIL, 10)
    userRepository.save(user)
  }
 
  ...
 
  @Test  void testFindById() {
    Optional<User> userOptional = userRepository.findById(user.id)
    assertTrue(userOptional.isPresent())
  }
 
  ...
 
}

这个测试用例启动了一个具有嵌入式 H2 数据库的实体管理器。H2 非常适合于测试, 因为它支持许多众所周知的数据库 (如 MySQL) 的兼容模式,可以模拟你的真实数据库。

下期预告:构建用户管理微服务(四):实现 REST 控制器

原文链接:https://www.springuni.com/user-management-microservice-part-3

no comments
Share

发表评论