将查询结果映射为对象
当 处理查询结果返回的值 时,需要手动提取它们的属性并转换为相应的 Java 类型。例如,要将 name 属性检索为字符串,需要使用 person.get("name").asString()。
借助驱动的 Value Mapping 功能,你可以声明一个 Java Record,描述查询预期返回的值的规格,并让驱动使用该类根据查询结果生成新对象。
将驱动值映射到本地类
要将记录映射为对象,请定义一个 Java Record,其组件需与查询返回的键完全一致。构造函数参数必须精确匹配查询返回键,且区分大小写。
最直接的做法是使用 Java Record,但使用一个普通类,只要其构造函数与查询返回键相匹配,也同样可行。无论哪种方式,都需要通过 Value.as() 方法把类定义提供给驱动。
:Person 节点映射到 Person 记录对象package demo;
import java.util.Map;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.QueryConfig;
public class App {
private static final String dbUri = "<database-uri>";
private static final String dbUser = "<username>";
private static final String dbPassword = "<password>";
public static void main(String... args) {
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {
record Person(String name, Integer age) {}
var persons = driver.executableQuery("MERGE (p:Person {name: $name, age: $age}) RETURN p")
.withParameters(Map.of("name", "Margarida", "age", 29))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(record -> record.get("p").as(Person.class))
.toList();
System.out.println(persons.get(0)); // Person[name=Margarida, age=29]
}
}
}
没有匹配属性的参数会得到 null 值。如果参数不接受 null(例如基本类型),则必须提供一个 可替代的构造函数,该构造函数不包含该参数。上例使用 Integer 而不是基本类型 int,以处理缺少 age 属性的节点。
在查询语句旁直接声明 record 对象是一种便捷的方式,能够快速获取易于提取属性的结果。但由于该类定义在局部作用域,其类型无法在方法之外引用。因此,此类不能作为方法的返回类型使用:要么在同一函数中处理映射后的值,要么确保它实现了在方法外部可访问的接口或父类。另一种做法是将 record 声明为类的 public 成员,或创建一个独立的类来包含你的 record 定义,这样映射后的对象即可在方法作用域之外使用。
|
虽然支持使用特定的复杂类型作为构造函数参数(例如 |
将属性映射为不同的名称 (@Property)
记录的属性名称与查询返回键可以不同。例如,考虑节点 (:Person {name: "Alice"})。查询 MERGE (p:Person {name: "Alice"}) RETURN p.name 返回的键是 p.name,即使属性名本身是 name。同理,查询 MERGE (pers:Person {name: "Alice"}) RETURN pers.name 的返回键为 pers.name。
你可以随时使用 Cypher 运算符 AS 修改返回键(例如 MERGE (p:Person {name: "Alice"}) RETURN p.name AS name),或者使用 @Property(<dbPropertyName>) 注解来指定后续构造函数参数应映射到的属性名。
name/age 映射到对象属性 firstName/Years// import org.neo4j.driver.mapping.Property;
record Person(@Property("name") String firstName, @Property("age") Integer Years) {}
var persons = driver.executableQuery("MERGE (p:Person {name: $name, age: $age}) RETURN p")
.withParameters(Map.of("name", "Margarida", "age", 29))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(record -> record.get("p").as(Person.class))
.toList();
System.out.println(persons.get(0)); // Person[firstName=Margarida, Years=29]
将驱动记录映射到本地类
前面的示例将驱动的 Value(例如标识为 p 的节点)映射到某个类。你同样可以使用映射功能对驱动的 Record 实例进行映射,方法是调用 Record.as() 方法。
name/p.age 映射到对象属性 Name/Age// import org.neo4j.driver.mapping.Property;
record Person(String name, @Property("p.age") Integer age) {}
var persons = driver.executableQuery("""
MERGE (p:Person {name: $name, age: $age})
RETURN p.name AS name, p.age
""")
.withParameters(Map.of("name", "Margarida", "age", 29))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(record -> record.as(Person.class))
.toList();
System.out.println(persons.get(0)); // Person[name=Margarida, age=29]
使用多个构造函数
你的 Java record 类可以包含多个构造函数。在这种情况下,驱动会依据以下规则(按优先级顺序)挑选合适的构造函数:
-
匹配属性最多
-
不匹配属性最少
构造函数必须至少有一个属性匹配,才能用于映射。
age 属性的额外构造函数// import org.neo4j.driver.mapping.Property;
record Person(String name, int age) {
public Person(@Property("name") String name) {
this(name, -1);
}
}
var persons = driver.executableQuery("MERGE (p:Person {name: $name}) RETURN p")
.withParameters(Map.of("name", "Axel"))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute()
.records()
.stream()
.map(record -> record.get("p").as(Person.class))
.toList();
|
除非使用编译器 在上面的示例中,仅包含 |
插入和更新数据
你也可以利用映射功能来插入或更新数据:先创建一个 Java Record 实例作为对象的蓝图,然后将其作为参数传递给查询。
:Person 节点record Person(String name, int age) {}
var person = new Person("Lucia", 29);
driver.executableQuery("CREATE (:Person $person)")
.withParameters(Map.of("person", person))
.execute();
var happyBirthday = new Person("Lucia", 30);
driver.executableQuery("""
MATCH (p:Person {name: $person.name})
SET p += $person
""")
.withParameters(Map.of("person", happyBirthday))
.execute();