此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data MongoDB 4.5.2! |
展开类型
未包装的实体用于在Java域模型中设计值对象,其属性被扁平化到父级的MongoDB文档中。
未包装的类型映射
请考虑以下域模型,其中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 | 加载name 属性,其值设置为null 如果两者firstname 和lastname 是null 或不在场。
通过使用onEmpty=USE_EMPTY 一个空的UserName ,具有潜力null value 的属性,将被创建。 |
对于不太冗长的可嵌入类型声明,请使用@Unwrapped.Nullable
和@Unwrapped.Empty
相反@Unwrapped(onEmpty = USE_NULL)
和@Unwrapped(onEmpty = USE_EMPTY)
.
这两个注释都使用 JSR-305 进行元注释@javax.annotation.Nonnull
以帮助进行可无空性检查。
可以在未包装的对象中使用复杂类型。 但是,这些字段本身不得是,也不得包含未包装的字段。 |
“未包装的类型”字段名称
可以使用可选的prefix
属性的@Unwrapped
注解。
通过剂量,将所选前缀附加到每个属性或@Field("…")
name 在未包装的对象中。
请注意,如果多个属性呈现为相同的字段名称,则值将相互覆盖。
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
在任何未包装的类型属性上。
@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) . |
查询未包装的对象
可以在类型级和字段级定义对未包装属性的查询,如提供的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 | 未包装对象上的场投影包括其所有属性。 |
对未包装对象的示例查询。
未包装的对象可以在Example
探测就像任何其他类型一样。
请查看“按示例查询”部分,了解有关此功能的更多信息。
存储库:对未包装对象的查询。
这Repository
抽象允许对未包装对象的字段以及整个对象进行查询。
interface UserRepository extends CrudRepository<User, String> {
List<User> findByName(UserName username); (1)
List<User> findByNameFirstname(String firstname); (2)
}
1 | 与未包装对象的所有字段匹配。 |
2 | 与firstname . |
即使存储库 |
对未包装对象进行更新
未包装的对象可以像属于域模型的任何其他对象一样更新。 映射层负责将结构展平到其周围环境。 可以更新未包装对象的单个属性以及整个值,如下面的示例所示。
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",
}
},
{ ... }
)
未包装对象上的聚合
聚合框架将尝试映射类型化聚合的未包装值。 请确保在引用其值之一时使用包括包装器对象在内的属性路径。 除此之外,不需要采取任何特殊行动。
未包装对象的索引
可以附加@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 | 为lastname 在users 收集。 |
2 | 无效@Indexed 与@Unwrapped |