使用Java Bolt驱动向AWS Neptune添加带日期属性边的报错问题
问题描述
我想用Java Bolt驱动给AWS Neptune图数据库添加边,边的一个属性需要存储日期时间信息。发现Neptune支持java.util.Date对象,但Bolt驱动不支持该类型,仅兼容java.time.LocalDateTime,但使用LocalDateTime时触发了内部错误,寻求解决办法。
环境信息
- Bolt驱动版本:4.3.3
- Java版本:Java 11(运行在AWS Lambda,无法升级更高版本)
示例代码
Maven依赖
<dependency> <groupId>org.neo4j.driver</groupId> <artifactId>neo4j-java-driver</artifactId> <version>4.3.3</version> </dependency>
写入数据的Java代码
Driver boltDriver = GraphDatabase.driver(neptuneConnectionString, Config.builder().withConnectionTimeout(30, TimeUnit.SECONDS) .withEncryption() .withTrustStrategy(Config.TrustStrategy.trustSystemCertificates()) .build()); Map<String, Object> parameters = new HashMap<>(); parameters.put("locationId", "dsfsdf3453"); parameters.put("deviceId", "fdfdf4534"); parameters.put("edgeId", "dfdf45345"); parameters.put("loginAt", LocalDateTime.now()); var query = "MERGE (l:Location_J {locationId: $locationId}) MERGE (d:Device_J {deviceId: $deviceId}) MERGE (l)-[a:ACCESSES_J {loginAt: $loginAt}]->(d)"; boltDriver.session().run(query, parameters);
触发的异常
WARNING: [0x06247997][uat-niyothon-neptune-2023.cluster-cfg5tfk5qker.ap-south-1.neptune.amazonaws.com:8182][021cdffffe623928-000035fc-0000b02d-f131b528da33cefe-05e79a93] Fatal error occurred in the pipeline org.neo4j.driver.exceptions.DatabaseException: "Unexpected server exception 'Operation terminated (internal error)'" at org.neo4j.driver.internal.util.ErrorUtil.newNeo4jError(ErrorUtil.java:90) at org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher.handleFailureMessage(InboundMessageDispatcher.java:108) at org.neo4j.driver.internal.messaging.common.CommonMessageReader.unpackFailureMessage(CommonMessageReader.java:83) at org.neo4j.driver.internal.messaging.common.CommonMessageReader.read(CommonMessageReader.java:59) at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:83) at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:35) at org.neo4j.driver.internal.shaded.io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) at org.neo4j.driver.internal.async.inbound.MessageDecoder.channelRead(MessageDecoder.java:47) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at org.neo4j.driver.internal.shaded.io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1368) at org.neo4j.driver.internal.shaded.io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1234) at org.neo4j.driver.internal.shaded.io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1280) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:507) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:446) at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at org.neo4j.driver.internal.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) at org.neo4j.driver.internal.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Unknown Source)
解决办法
方案1:将LocalDateTime转换为Instant(带时区的时间戳)
Neptune对带时区的时间类型支持更稳定,LocalDateTime无时区信息容易引发解析错误。将其转换为Instant后传入参数:
// 替换原有的loginAt参数设置 parameters.put("loginAt", LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
可配合修改Cypher查询明确类型转换(更稳妥):
MERGE (l:Location_J {locationId: $locationId}) MERGE (d:Device_J {deviceId: $deviceId}) MERGE (l)-[a:ACCESSES_J {loginAt: datetime($loginAt)}]->(d)
方案2:使用java.util.Date配合驱动自定义类型映射
通过自定义类型转换器让Bolt驱动兼容java.util.Date:
- 实现
TypeAdapter接口处理Date与Neptune时间类型的转换:
public class DateTypeAdapter implements TypeAdapter<Date> { @Override public Value toValue(Date value) { return Values.value(value.toInstant()); } @Override public Date fromValue(Value value) { return Date.from(value.asInstant()); } }
- 创建Driver时注册该适配器:
Config config = Config.builder() .withConnectionTimeout(30, TimeUnit.SECONDS) .withEncryption() .withTrustStrategy(Config.TrustStrategy.trustSystemCertificates()) .withCustomTypeAdapter(Date.class, new DateTypeAdapter()) .build(); Driver boltDriver = GraphDatabase.driver(neptuneConnectionString, config);
- 直接使用
java.util.Date作为参数:
parameters.put("loginAt", new Date());
方案3:转换为ISO-8601字符串传递
将时间转为标准ISO-8601字符串,在Cypher查询中转换为datetime类型:
// 生成ISO-8601格式字符串 DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; parameters.put("loginAt", LocalDateTime.now().format(formatter));
修改Cypher查询:
MERGE (l:Location_J {locationId: $locationId}) MERGE (d:Device_J {deviceId: $deviceId}) MERGE (l)-[a:ACCESSES_J {loginAt: datetime($loginAt)}]->(d)
关键原因说明
Neptune的Bolt兼容层对无时区的LocalDateTime处理存在兼容性问题,带时区的Instant或标准ISO字符串能更好匹配Neptune的datetime类型要求,避免内部解析错误。
内容的提问来源于stack exchange,提问作者Vishal Jamdade
相关产品推荐
相关产品推荐

