此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data MongoDB 4.4.0! |
解包类型
未包装的实体用于设计 Java 域模型中的值对象,其属性被扁平化为父级的 MongoDB Document。
未包类型映射
请考虑以下域模型,其中用 .
注释表示 的所有属性都应该被拼合到拥有该属性的文档中。User.name
@Unwrapped
@Unwrapped
UserName
user
name
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 | 加载属性时,其值设置为 if both 和 are either or not exist。
通过使用空 ,将创建具有其属性潜在值的 。name null firstname lastname null onEmpty=USE_EMPTY UserName null |
对于不太详细的可嵌入类型声明,请使用 and 代替 和 。
这两个注解都使用 JSR-305 进行元注解,以帮助进行可空性检查。@Unwrapped.Nullable
@Unwrapped.Empty
@Unwrapped(onEmpty = USE_NULL)
@Unwrapped(onEmpty = USE_EMPTY)
@javax.annotation.Nonnull
可以在未包的对象中使用复杂类型。 但是,这些字段本身不得包含未包装的字段。 |
Unwrapped Types 字段名称
可以使用 annotation 的 optional 属性多次解包一个 value 对象。
通过剂量,以便将所选前缀添加到解包对象中的每个属性或名称之前。
请注意,如果多个属性呈现为相同的字段名称,则值将相互覆盖。prefix
@Unwrapped
@Field("…")
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_ |
虽然将 annotation 与 合并到同一个属性上没有意义,因此会导致错误。
这是对任何 unwrapped types 属性使用的完全有效的方法。@Field
@Unwrapped
@Field
@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(prefix) @Field(name) |
对 Unwrapped Objects 的查询
可以在类型级别和字段级别定义对 unwrapped 属性的查询,因为提供的 content 与域类型匹配。
在呈现实际查询时,将考虑前缀和可能的自定义字段名称。
使用未打包对象的属性名称与所有包含的字段匹配,如以下示例所示。Criteria
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"
})
也可以使用其属性名称直接寻址未包装对象的任何字段,如下面的代码片段所示。
Query findByUserFirstName = query(where("name.firstname").is("Shuri"));
List<User> users = template.findAll(findByUserFirstName, User.class);
db.collection.find({
"firstname" : "Shuri"
})
按未包装的字段排序。
未包包对象的字段可用于通过其属性路径进行排序,如以下示例所示。
Query findByUserLastName = query(where("name.lastname").is("Romanoff"));
List<User> user = template.findAll(findByUserName.withSort(Sort.by("name.firstname")), User.class);
db.collection.find({
"lastname" : "Romanoff"
}).sort({ "firstname" : 1 })
尽管有可能,但使用未包装的对象本身作为排序条件会以不可预测的顺序包含其所有字段,并且可能会导致排序不准确。 |
展开对象上的场投影
解包对象的字段可以作为整体或通过单个字段进行投影,如以下示例所示。
Query findByUserLastName = query(where("name.firstname").is("Gamora"));
findByUserLastName.fields().include("name"); (1)
List<User> user = template.findAll(findByUserName, User.class);
db.collection.find({
"lastname" : "Gamora"
},
{
"firstname" : 1,
"lastname" : 1
})
1 | 未包对象上的字段投影包括其所有属性。 |
Query findByUserLastName = query(where("name.lastname").is("Smoak"));
findByUserLastName.fields().include("name.firstname"); (1)
List<User> user = template.findAll(findByUserName, User.class);
db.collection.find({
"lastname" : "Smoak"
},
{
"firstname" : 1
})
1 | 未包对象上的字段投影包括其所有属性。 |
Query By Example 对未包装的对象。
Unwrapped objects 可以在 probe 中使用,就像任何其他类型的 its.
请查看 Query By Example 部分,以了解有关此功能的更多信息。Example
存储库 对未打包对象的查询。
抽象允许对未包装对象的字段以及整个对象进行派生查询。Repository
interface UserRepository extends CrudRepository<User, String> {
List<User> findByName(UserName username); (1)
List<User> findByNameFirstname(String firstname); (2)
}
1 | 与未打包对象的所有字段匹配。 |
2 | 与 .firstname |
即使 repository namespace 属性设置为 ,也会暂停为未打包的对象创建索引。 |
更新已解包的对象
解包的对象可以像属于域模型的任何其他对象一样进行更新。 映射层负责将结构展平到其周围环境中。 可以更新未包装对象的单个属性以及整个值,如以下示例所示。
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" }
},
{ ... }
)
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 将尝试映射类型化聚合的未包装值。 请确保在引用其值之一时使用包含包装器对象的属性路径。 除此之外,不需要执行任何特殊操作。
未包装对象上的索引
可以将 Comments 附加到 unwrapped 类型的属性,就像对常规对象所做的那样。
不能与 owning 属性上的 Comments 一起使用。@Indexed
@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 | 为 in collection 创建的索引。lastname users |
2 | 无效用法以及@Indexed @Unwrapped |