【Spring连载】使用Spring Data访问 MongoDB----对象映射之非包装类型
温馨提示:这篇文章已超过394天没有更新,请注意相关的内容是否还可用!
【Spring连载】使用Spring Data访问 MongoDB----对象映射之非包装类型
- 一、未包装类型映射
- 二、未包装类型字段名
- 三、查询未包装对象
- 3.1 按未包装字段排序
- 3.2 未包装对象的字段投影
- 3.3 未包装对象的Query By Example
- 3.4 未包装对象的存储库查询
- 四、更新未包装对象
- 五、未包装对象上的聚合
- 六、未包装对象上的索引
未包装实体用于在Java领域模型中设计值对象,其属性被展平(flattened)到父类的MongoDB Document中。
一、未包装类型映射
考虑下面的域模型,其中User.name用@Unwrapped注解。@Unwrapped注解表示UserName的所有属性都应该被展平到拥有name属性的user document中。
(图片来源网络,侵删)例1:展开对象的示例代码
class User { @Id String userId; @Unwrapped(onEmpty = USE_NULL) --------1 UserName name; } class UserName { String firstname; String lastname; }{ "_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:带有名称前缀的展开对象的示例代码
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; }{ "_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注解的展开对象示例代码
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; }{ "_id" : "2647f7b9-89da", "u-first-name" : "Barbara", --------2 "u-last-name" : "Gordon" } 1. UserName的所有属性都以u-为前缀。 2. 最终字段名是@Unwrapped(前缀)和@Field(名称)拼接的结果。三、查询未包装对象
可以在类型和字段级别上对展开的属性定义查询,因为提供的条件与域类型匹配。在呈现实际查询时,将考虑前缀和潜在的自定义字段名。使用展开对象的属性名称来匹配所有包含的字段,如下面的示例所示。
例4:查询未包装对象
UserName userName = new UserName("Carol", "Danvers") Query findByUserName = query(where("name").is(userName)); User user = template.findOne(findByUserName, User.class);db.collection.find({ "firstname" : "Carol", "lastname" : "Danvers" })也可以直接使用未包装对象的属性名来处理它的任何字段,如下面的代码片段所示。
例5:查询未包装对象的字段
Query findByUserFirstName = query(where("name.firstname").is("Shuri")); List users = template.findAll(findByUserFirstName, User.class);db.collection.find({ "firstname" : "Shuri" })3.1 按未包装字段排序
未包装对象的字段可用于通过其属性路径进行排序,如下面的示例所示。
例6:对未包装字段进行排序
Query findByUserLastName = query(where("name.lastname").is("Romanoff")); List user = template.findAll(findByUserName.withSort(Sort.by("name.firstname")), User.class);db.collection.find({ "lastname" : "Romanoff" }).sort({ "firstname" : 1 })虽然可以,但使用未包装对象本身作为排序条件会以不可预测的顺序包含其所有字段,并可能导致不准确的排序。
3.2 未包装对象的字段投影
未包装对象的字段可以作为一个整体或通过单个字段进行投影,如下面的示例所示。
例7:未包装对象上的投影
Query findByUserLastName = query(where("name.firstname").is("Gamora")); findByUserLastName.fields().include("name"); --------1 List user = template.findAll(findByUserName, User.class);db.collection.find({ "lastname" : "Gamora" }, { "firstname" : 1, "lastname" : 1 }) 1. 未包装对象上的字段投影包括其所有属性。例8:未包装对象的字段上的投影
Query findByUserLastName = query(where("name.lastname").is("Smoak")); findByUserLastName.fields().include("name.firstname"); --------1 List user = template.findAll(findByUserName, User.class);db.collection.find({ "lastname" : "Smoak" }, { "firstname" : 1 }) 1. 未包装对象上的字段投影包括其所有属性。3.3 未包装对象的Query By Example
未包装对象可以像任何其他类型一样在Example probe中使用。请查看Query By Example一节,以了解有关此特性的更多信息。
3.4 未包装对象的存储库查询
Repository抽象允许对未包装对象的字段以及整个对象派生查询。
例9:对未包装对象的存储库查询
interface UserRepository extends CrudRepository { List findByName(UserName username); --------1 List findByNameFirstname(String firstname); --------2 } 1. 对未包装对象的所有字段进行匹配。 2. 与firstname匹配。即使存储库create-query-indexes命名空间属性设置为true,也会挂起未包装对象的索引创建。
四、更新未包装对象
未包装的对象可以作为域模型的一部分的任何其他对象进行更新。映射层负责将结构平铺到周围环境中(surroundings)。可以更新未包装对象的单个属性以及整个值,如下面的示例所示。
例10:更新未包装对象的单个字段
Update update = new Update().set("name.firstname", "Janet"); template.update(User.class).matching(where("id").is("Wasp")) .apply(update).first()db.collection.update({ "_id" : "Wasp" }, { "$set" { "firstname" : "Janet" } }, { ... } )例11:更新未包装的对象
Update update = new Update().set("name", new Name("Janet", "van Dyne")); template.update(User.class).matching(where("id").is("Wasp")) .apply(update).first()db.collection.update({ "_id" : "Wasp" }, { "$set" { "firstname" : "Janet", "lastname" : "van Dyne", } }, { ... } )五、未包装对象上的聚合
Aggregation Framework将尝试映射类型聚合的展开值。引用某个值时,请确保使用包含包装对象的属性路径。除此之外,不需要采取任何特别行动。
六、未包装对象上的索引
可以将@Indexed注解附加到未包装类型的属性上,就像处理普通对象一样。不可能在所属(owning)属性上同时使用@Indexed和@Unwrapped注解。
public class User { @Id private String userId; @Unwrapped(onEmpty = USE_NULL) UserName name; --------1 // Invalid -> InvalidDataAccessApiUsageException @Indexed --------2 @Unwrapped(onEmpty = USE_Empty) Address address; } public class UserName { private String firstname; @Indexed private String lastname; --------1 } 1. 为用户集合中的lastname创建的索引。 2. 和@Unwrapped一起的无效的@Indexed用法
