|
| 1 | +[[conversions]] |
| 2 | += Conversions |
| 3 | + |
| 4 | +[[build-in.conversions]] |
| 5 | +== Convention-based Mapping |
| 6 | + |
| 7 | +The Neo4j Converter has a few conventions for mapping objects when no additional mapping metadata is provided. |
| 8 | +The conventions are: |
| 9 | + |
| 10 | +* The short Java class name is mapped to the primary label in the following manner: |
| 11 | +The class `com.bigbank.SavingsAccount` maps to the `savingsAccount` primary label. |
| 12 | +* The converter uses any <<custom.conversions,Spring Converter>> registered with it to override the default mapping of object properties to node fields and values. |
| 13 | +* The fields of an object are used to convert to and from fields in the graph. |
| 14 | +Public `JavaBean` properties are not used. |
| 15 | +* If you have a single non-zero-argument constructor whose constructor argument names match top-level property names of node, that constructor is used. |
| 16 | +Otherwise, the zero-argument constructor is used. |
| 17 | +If there is more than one non-zero-argument constructor, an exception will be thrown. |
| 18 | + |
| 19 | +We support a broad range of conversions out of the box. |
| 20 | +Find the list of supported cypher types in the official drivers manual: https://neo4j.com/docs/java-manual/current/cypher-workflow/#java-driver-type-mapping[Type mapping]. |
| 21 | + |
| 22 | +Primitive types of wrapper types are equally supported. |
| 23 | + |
| 24 | +[cols="3,3,1",options="header"] |
| 25 | +|=== |
| 26 | +|Domain type|Cypher type|Maps directly to native type |
| 27 | + |
| 28 | +|`java.lang.Boolean` |
| 29 | +|Boolean |
| 30 | +|✔ |
| 31 | + |
| 32 | +|`boolean[]` |
| 33 | +|List of Boolean |
| 34 | +|✔ |
| 35 | + |
| 36 | +|`java.lang.Long` |
| 37 | +|Integer |
| 38 | +|✔ |
| 39 | + |
| 40 | +|`long[]` |
| 41 | +|List of Integer |
| 42 | +|✔ |
| 43 | + |
| 44 | +|`java.lang.Double` |
| 45 | +|Float |
| 46 | +|✔ |
| 47 | + |
| 48 | +|`double[]` |
| 49 | +|List of Float |
| 50 | +|✔ |
| 51 | + |
| 52 | +|`java.lang.String` |
| 53 | +|String |
| 54 | +|✔ |
| 55 | + |
| 56 | + |
| 57 | +|`java.lang.String[]` |
| 58 | +|List of String |
| 59 | +|✔ |
| 60 | + |
| 61 | +|`byte[]` |
| 62 | +|ByteArray |
| 63 | +|✔ |
| 64 | + |
| 65 | +|`java.lang.Byte` |
| 66 | +|ByteArray with length 1 |
| 67 | +| |
| 68 | + |
| 69 | +|`java.lang.Character` |
| 70 | +|String with length 1 |
| 71 | +| |
| 72 | + |
| 73 | +|`char[]` |
| 74 | +|List of String with length 1 |
| 75 | +| |
| 76 | + |
| 77 | +|`java.util.Date` |
| 78 | +|String formatted as ISO 8601 Date (`yyyy-MM-dd'T'HH:mm:ss.SSSZ`). |
| 79 | +Notice the `Z`: SDN will store all `java.util.Date` instances in `UTC`. |
| 80 | +If you require the time zone, use a type that supports it (i.e. `ZoneDateTime`) or store the zone as a separate property. |
| 81 | +| |
| 82 | + |
| 83 | +|`java.lang.Float` |
| 84 | +|String |
| 85 | +| |
| 86 | + |
| 87 | +|`float[]` |
| 88 | +|List of String |
| 89 | +| |
| 90 | + |
| 91 | +|`java.lang.Integer` |
| 92 | +|Integer |
| 93 | +| |
| 94 | + |
| 95 | +|`int[]` |
| 96 | +|List of Integer |
| 97 | +| |
| 98 | + |
| 99 | +|`java.util.Locale` |
| 100 | +|String formatted as BCP 47 language tag |
| 101 | +| |
| 102 | + |
| 103 | +|`java.lang.Short` |
| 104 | +|Integer |
| 105 | +| |
| 106 | + |
| 107 | +|`short[]` |
| 108 | +|List of Integer |
| 109 | +| |
| 110 | + |
| 111 | +|`java.math.BigDecimal` |
| 112 | +|String |
| 113 | +| |
| 114 | + |
| 115 | +|`java.math.BigInteger` |
| 116 | +|String |
| 117 | +| |
| 118 | + |
| 119 | +|`java.time.LocalDate` |
| 120 | +|Date |
| 121 | +|✔ |
| 122 | + |
| 123 | +|`java.time.OffsetTime` |
| 124 | +|Time |
| 125 | +|✔ |
| 126 | + |
| 127 | +|`java.time.LocalTime` |
| 128 | +|LocalTime |
| 129 | +|✔ |
| 130 | + |
| 131 | +|`java.time.ZonedDateTime` |
| 132 | +|DateTime |
| 133 | +|✔ |
| 134 | + |
| 135 | +|`java.time.LocalDateTime` |
| 136 | +|LocalDateTime |
| 137 | +|✔ |
| 138 | + |
| 139 | +|`java.time.OffsetDateTime` |
| 140 | +|DateTime |
| 141 | +| |
| 142 | + |
| 143 | +|`java.time.Instant` |
| 144 | +|DateTime |
| 145 | +| |
| 146 | + |
| 147 | +|`java.util.TimeZone` |
| 148 | +|String |
| 149 | +| |
| 150 | + |
| 151 | +|`java.time.ZoneId` |
| 152 | +|String |
| 153 | +| |
| 154 | + |
| 155 | +|`java.time.Period` |
| 156 | +|Duration |
| 157 | +| |
| 158 | + |
| 159 | +|`java.time.Duration` |
| 160 | +|Duration |
| 161 | +| |
| 162 | + |
| 163 | +|`org.neo4j.driver.types.IsoDuration` |
| 164 | +|Duration |
| 165 | +|✔ |
| 166 | + |
| 167 | +|`org.neo4j.driver.types.Point` |
| 168 | +|Point |
| 169 | +|✔ |
| 170 | + |
| 171 | +|`org.springframework.data.neo4j.types.GeographicPoint2d` |
| 172 | +|Point with CRS 4326 |
| 173 | +| |
| 174 | + |
| 175 | +|`org.springframework.data.neo4j.types.GeographicPoint3d` |
| 176 | +|Point with CRS 4979 |
| 177 | +| |
| 178 | + |
| 179 | +|`org.springframework.data.neo4j.types.CartesianPoint2d` |
| 180 | +|Point with CRS 7203 |
| 181 | +| |
| 182 | + |
| 183 | +|`org.springframework.data.neo4j.types.CartesianPoint3d` |
| 184 | +|Point with CRS 9157 |
| 185 | +| |
| 186 | + |
| 187 | +|`org.springframework.data.geo.Point` |
| 188 | +|Point with CRS 4326 and x/y corresponding to lat/long |
| 189 | +| |
| 190 | + |
| 191 | +|Instances of `Enum` |
| 192 | +|String (The name value of the enum) |
| 193 | +| |
| 194 | + |
| 195 | +|Instances of `Enum[]` |
| 196 | +|List of String (The name value of the enum) |
| 197 | +| |
| 198 | + |
| 199 | +|`java.net.URL` |
| 200 | +|String |
| 201 | +| |
| 202 | + |
| 203 | +|`java.net.URI` |
| 204 | +|String |
| 205 | +| |
| 206 | + |
| 207 | +|`java.util.UUID` |
| 208 | +|String |
| 209 | +| |
| 210 | + |
| 211 | +|=== |
| 212 | + |
| 213 | +[[custom.conversions]] |
| 214 | +== Custom conversions |
| 215 | + |
| 216 | +[[custom.conversions.attribute.types]] |
| 217 | +=== For attributes of a given type |
| 218 | + |
| 219 | +If you prefer to work with your own types in the entities or as parameters for `@Query` annotated methods, you can define and provide a custom converter implementation. |
| 220 | +First you have to implement a `GenericConverter` and register the types your converter should handle. |
| 221 | +For entity property type converters you need to take care of converting your type to *and* from a Neo4j Java Driver `Value`. |
| 222 | +If your converter is supposed to work only with custom query methods in the repositories, it is sufficient to provide the one-way conversion to the `Value` type. |
| 223 | + |
| 224 | +.Example of a custom converter implementation |
| 225 | +[source,java,indent=0] |
| 226 | +---- |
| 227 | +include::example$documentation/repositories/conversion/MyCustomTypeConverter.java[tag=custom-converter.implementation] |
| 228 | +---- |
| 229 | + |
| 230 | +To make SDN aware of your converter, it has to be registered in the `Neo4jConversions`. |
| 231 | +To do this, you have to create a `@Bean` with the type `org.springframework.data.neo4j.core.convert.Neo4jConversions`. |
| 232 | +Otherwise, the `Neo4jConversions` will get created in the background with the internal default converters only. |
| 233 | + |
| 234 | +.Example of a custom converter implementation |
| 235 | +[source,java,indent=0] |
| 236 | +---- |
| 237 | +include::example$documentation/repositories/conversion/MyCustomTypeConverter.java[tag=custom-converter.neo4jConversions] |
| 238 | +---- |
| 239 | + |
| 240 | +If you need multiple converters in your application, you can add as many as you need in the `Neo4jConversions` constructor. |
| 241 | + |
| 242 | +[[custom.conversions.attribute.specific]] |
| 243 | +=== For specific attributes only |
| 244 | + |
| 245 | +If you need conversions only for some specific attributes, we provide `@ConvertWith`. |
| 246 | +This is an annotation that can be put on attributes of both entities (`@Node`) and relationship properties (`@RelationshipProperties`) |
| 247 | +It defines a `Neo4jPersistentPropertyConverter` via the `converter` attribute |
| 248 | +and an optional `Neo4jPersistentPropertyConverterFactory` to construct the former. |
| 249 | +With an implementation of `Neo4jPersistentPropertyConverter` all specific conversions for a given type can be addressed. |
| 250 | +In addition, `@ConvertWith` also provides `converterRef` for referencing any Spring bean in the application context implementing |
| 251 | +`Neo4jPersistentPropertyConverter`. The referenced bean will be preferred over constructing a new converter. |
| 252 | + |
| 253 | +We provide `@DateLong` and `@DateString` as meta-annotated annotations for backward compatibility with Neo4j-OGM schemes not using native types. |
| 254 | +Those are meta annotated annotations building on the concept above. |
| 255 | + |
| 256 | +[[custom.conversions.composite-properties]] |
| 257 | +==== Composite properties |
| 258 | + |
| 259 | +With `@CompositeProperty`, attributes of type `Map<String, Object>` or `Map<? extends Enum, Object>` can be stored as composite properties. |
| 260 | +All entries inside the map will be added as properties to the node or relationship containing the property. |
| 261 | +Either with a configured prefix or prefixed with the name of the property. |
| 262 | +While we only offer that feature for maps out of the box, you can `Neo4jPersistentPropertyToMapConverter` and configure it |
| 263 | +as the converter to use on `@CompositeProperty`. A `Neo4jPersistentPropertyToMapConverter` needs to know how a given type can |
| 264 | +be decomposed to and composed back from a map. |
0 commit comments