【Spring连载】使用Spring Data访问 MongoDB----对象映射之非包装类型
- 一、未包装类型映射
- 二、未包装类型字段名
- 三、查询未包装对象
-
- [3.1 按未包装字段排序](#3.1 按未包装字段排序)
- [3.2 未包装对象的字段投影](#3.2 未包装对象的字段投影)
- [3.3 未包装对象的Query By Example](#3.3 未包装对象的Query By Example)
- [3.4 未包装对象的存储库查询](#3.4 未包装对象的存储库查询)
- 四、更新未包装对象
- 五、未包装对象上的聚合
- 六、未包装对象上的索引
未包装实体用于在Java领域模型中设计值对象,其属性被展平(flattened)到父类的MongoDB Document中。
一、未包装类型映射
考虑下面的域模型,其中User.name用@Unwrapped注解。@Unwrapped注解表示UserName的所有属性都应该被展平到拥有name属性的user document中。
例1:展开对象的示例代码
java
class User {
@Id
String userId;
@Unwrapped(onEmpty = USE_NULL) --------1
UserName name;
}
class UserName {
String firstname;
String lastname;
}
json
{
"_id" : "1da2ba06-3ba7",
"firstname" : "Emma",
"lastname" : "Frost"
}
1. 加载name属性时,如果firstname和lastname都为null或不存在,则其值设置为null。通过使用onEmpty=USE_EMPTY,将创建一个空的UserName,其属性可能为null值。
对于不太详细的可嵌入类型声明,请使用"@Unwrapped.Nullable"和"@Unwrapped.Empty",而不是"@Unwrapped(onEmpty = USE_NULL)"和"@Unwrapped(onEmpty = USE_EMPTY)"。这两个注解都使用JSR-305"@javax.annotation.Nonnull"进行元注解,以帮助进行可空性检查。
可以在展开的对象中使用复杂类型。但是,这些字段本身不能是,也不能包含未展开的字段。
二、未包装类型字段名
通过使用@Unwrapped注解的可选前缀属性,可以多次展开值对象。通过这样做,所选择的前缀被附加到未包装对象中的每个属性或@Field("...")名称之前。请注意,如果多个属性呈现为相同的字段名称,则值将相互覆盖。
例2:带有名称前缀的展开对象的示例代码
java
class User {
@Id
String userId;
@Unwrapped.Nullable(prefix = "u_") --------1
UserName name;
@Unwrapped.Nullable(prefix = "a_") --------2
UserName name;
}
class UserName {
String firstname;
String lastname;
}
json
{
"_id" : "a6a805bd-f95f",
"u_firstname" : "Jean", --------1
"u_lastname" : "Grey",
"a_firstname" : "Something", --------2
"a_lastname" : "Else"
}
1. UserName的所有属性都以u_为前缀。
2. UserName的所有属性都以a_为前缀。
在同一属性上组合@Field注解和@Unwrapped是没有意义的,因此会导致错误。在任何展开的类型属性上使用@Field是完全有效的方法。
例3:带有@Field注解的展开对象示例代码
java
public class User {
@Id
private String userId;
@Unwrapped.Nullable(prefix = "u-") --------1
UserName name;
}
public class UserName {
@Field("first-name") --------2
private String firstname;
@Field("last-name")
private String lastname;
}
json
{
"_id" : "2647f7b9-89da",
"u-first-name" : "Barbara", --------2
"u-last-name" : "Gordon"
}
1. UserName的所有属性都以u-为前缀。
2. 最终字段名是@Unwrapped(前缀)和@Field(名称)拼接的结果。
三、查询未包装对象
可以在类型和字段级别上对展开的属性定义查询,因为提供的条件与域类型匹配。在呈现实际查询时,将考虑前缀和潜在的自定义字段名。使用展开对象的属性名称来匹配所有包含的字段,如下面的示例所示。
例4:查询未包装对象
java
UserName userName = new UserName("Carol", "Danvers")
Query findByUserName = query(where("name").is(userName));
User user = template.findOne(findByUserName, User.class);
json
db.collection.find({
"firstname" : "Carol",
"lastname" : "Danvers"
})
也可以直接使用未包装对象的属性名来处理它的任何字段,如下面的代码片段所示。
例5:查询未包装对象的字段
java
Query findByUserFirstName = query(where("name.firstname").is("Shuri"));
List<User> users = template.findAll(findByUserFirstName, User.class);
json
db.collection.find({
"firstname" : "Shuri"
})
3.1 按未包装字段排序
未包装对象的字段可用于通过其属性路径进行排序,如下面的示例所示。
例6:对未包装字段进行排序
java
Query findByUserLastName = query(where("name.lastname").is("Romanoff"));
List<User> user = template.findAll(findByUserName.withSort(Sort.by("name.firstname")), User.class);
json
db.collection.find({
"lastname" : "Romanoff"
}).sort({ "firstname" : 1 })
虽然可以,但使用未包装对象本身作为排序条件会以不可预测的顺序包含其所有字段,并可能导致不准确的排序。
3.2 未包装对象的字段投影
未包装对象的字段可以作为一个整体或通过单个字段进行投影,如下面的示例所示。
例7:未包装对象上的投影
java
Query findByUserLastName = query(where("name.firstname").is("Gamora"));
findByUserLastName.fields().include("name"); --------1
List<User> user = template.findAll(findByUserName, User.class);
json
db.collection.find({
"lastname" : "Gamora"
},
{
"firstname" : 1,
"lastname" : 1
})
1. 未包装对象上的字段投影包括其所有属性。
例8:未包装对象的字段上的投影
java
Query findByUserLastName = query(where("name.lastname").is("Smoak"));
findByUserLastName.fields().include("name.firstname"); --------1
List<User> user = template.findAll(findByUserName, User.class);
json
db.collection.find({
"lastname" : "Smoak"
},
{
"firstname" : 1
})
1. 未包装对象上的字段投影包括其所有属性。
3.3 未包装对象的Query By Example
未包装对象可以像任何其他类型一样在Example probe中使用。请查看Query By Example一节,以了解有关此特性的更多信息。
3.4 未包装对象的存储库查询
Repository抽象允许对未包装对象的字段以及整个对象派生查询。
例9:对未包装对象的存储库查询
java
interface UserRepository extends CrudRepository<User, String> {
List<User> findByName(UserName username); --------1
List<User> findByNameFirstname(String firstname); --------2
}
1. 对未包装对象的所有字段进行匹配。
2. 与firstname匹配。
即使存储库create-query-indexes命名空间属性设置为true,也会挂起未包装对象的索引创建。