mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-09-20 11:17:36 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7fddf1981 | ||
|
|
f610d03a1d | ||
|
|
852f199ffb | ||
|
|
56d33b1f1b | ||
|
|
e9b00cbbd1 | ||
|
|
230d7a36a7 | ||
|
|
10bf5dbbbd | ||
|
|
bffe741642 | ||
|
|
a8189567dd | ||
|
|
313fdda857 |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-clickhouse</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-db2</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-hana</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mariadb</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mysql</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-net-postgis-types</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-nuodb</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-oracle</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-pgvector-types</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlite</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -41,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -60,13 +60,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-all</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>composites</artifactId>
|
||||
|
||||
@@ -183,6 +183,8 @@ database.save(customer);
|
||||
| Configure database and `Database` bean | [add-ebean-postgres-database-config.md](guides/add-ebean-postgres-database-config.md) |
|
||||
| Add PostgreSQL test container support | [add-ebean-postgres-test-container.md](guides/add-ebean-postgres-test-container.md) |
|
||||
| Generate DB migrations | [add-ebean-db-migration-generation.md](guides/add-ebean-db-migration-generation.md) |
|
||||
| Migrate JSON APIs from Jackson core to avaje-json-core | [migrating-json-jackson-core-to-avaje-json-core.md](guides/migrating-json-jackson-core-to-avaje-json-core.md) |
|
||||
| Know which `@DbJson` types need Jackson vs built-in | [dbjson-mapping-support.md](guides/dbjson-mapping-support.md) |
|
||||
| Model entity beans correctly | [entity-bean-creation.md](guides/entity-bean-creation.md) |
|
||||
| Use Lombok safely with entities | [lombok-with-ebean-entity-beans.md](guides/lombok-with-ebean-entity-beans.md) |
|
||||
| Write type-safe query bean queries | [writing-ebean-query-beans.md](guides/writing-ebean-query-beans.md) |
|
||||
|
||||
@@ -12,6 +12,7 @@ Key guides (fetch and follow when performing the relevant task):
|
||||
- Maven POM setup: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-postgres-maven-pom.md
|
||||
- Database configuration: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-postgres-database-config.md
|
||||
- Migrate to `Database.builder()`: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/migrating-to-database-builder.md
|
||||
- Migrate JSON APIs from Jackson core to avaje-json-core: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/migrating-json-jackson-core-to-avaje-json-core.md
|
||||
- Write queries with query beans: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/writing-ebean-query-beans.md
|
||||
- Persisting and transactions: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/persisting-and-transactions-with-ebean.md
|
||||
- Query metrics and naming: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/ebean-query-metrics.md
|
||||
|
||||
@@ -23,6 +23,7 @@ existing Maven project. Complete the steps in order.
|
||||
| Guide | Description |
|
||||
|-------|-------------|
|
||||
| [Migrate to `Database.builder()`](migrating-to-database-builder.md) | Replace legacy `new DatabaseConfig()` and `DatabaseFactory.create(...)` code with `Database.builder()` and `DatabaseBuilder.build()`. Includes common rewrites, fluent builder equivalents, and manual-review cases for semi-automated upgrades |
|
||||
| [Migrate JSON APIs from Jackson core to avaje-json-core](migrating-json-jackson-core-to-avaje-json-core.md) | Cut over `JsonParser`/`JsonGenerator`/`JsonFactory` usage to `JsonReader`/`JsonWriter`/`JsonStream`, including `DatabaseBuilder`/`DatabaseConfig` JSON config changes and validation checklist |
|
||||
|
||||
## Observability
|
||||
|
||||
@@ -38,6 +39,7 @@ existing Maven project. Complete the steps in order.
|
||||
|-------|-------------|
|
||||
| [Entity Bean Creation](entity-bean-creation.md) | How to generate clean, idiomatic Ebean entity beans for AI agents; patterns and anti-patterns; field visibility and accessor guidance; minimal boilerplate |
|
||||
| [Lombok with Ebean entity beans](lombok-with-ebean-entity-beans.md) | Which Lombok annotations to use and avoid on entity beans; why `@Data` is incompatible with Ebean; how to use `@Getter` + `@Setter` + `@Accessors(chain = true)` |
|
||||
| [`@DbJson` mapping support (built-in vs Jackson)](dbjson-mapping-support.md) | Which `@DbJson` / `@DbJsonB` property types are handled by the built-in avaje-json-core support versus which require `ebean-jackson-mapper` (Jackson `ObjectMapper`); supported `String`/`List`/`Set`/`Map` matrix; enum-key and `@DbArray` notes |
|
||||
|
||||
## Querying
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# Guide: `@DbJson` / `@DbJsonB` mapping support — built-in vs Jackson ObjectMapper
|
||||
|
||||
## Purpose
|
||||
|
||||
Ebean can map `@DbJson` and `@DbJsonB` properties in two ways:
|
||||
|
||||
- **Built-in** JSON support, backed by **avaje-json-core** — no extra dependency.
|
||||
- **Jackson `ObjectMapper`**, provided by the **`ebean-jackson-mapper`** module — used
|
||||
for everything the built-in support does not handle.
|
||||
|
||||
This guide lists exactly which property types are handled built-in and which require
|
||||
`ebean-jackson-mapper`.
|
||||
|
||||
> If a property type is **not** handled built-in and `ebean-jackson-mapper` is not on the
|
||||
> classpath, Ebean fails fast at startup:
|
||||
>
|
||||
> ```text
|
||||
> Unsupported @DbJson mapping - Missing dependency ebean-jackson-mapper?
|
||||
> Jackson ObjectMapper not present for <property>
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Property type | Built-in (avaje-json-core) | Needs `ebean-jackson-mapper` |
|
||||
|---|:---:|:---:|
|
||||
| `String` | ✅ | |
|
||||
| `List<String>`, `List<Long>` | ✅ | |
|
||||
| `Set<String>`, `Set<Long>` | ✅ | |
|
||||
| `Map<String, Object>`, `Map<String, ?>` | ✅ | |
|
||||
| `Map<String, String>` | ✅ | |
|
||||
| `Map<Enum, Object>`, `Map<Enum, String>` | ✅ | |
|
||||
| `List`/`Set` of any other element type (`Integer`, `Double`, `UUID`, `LocalDate`, an enum, a POJO, …) | | ✅ |
|
||||
| `Map` with a typed value other than `String`/`Object` (`Map<String,Integer>`, `Map<String,UUID>`, …) | | ✅ |
|
||||
| `Map` with a key other than `String` or an enum (`Map<Integer, …>`, `Map<UUID, …>`) | | ✅ |
|
||||
| POJOs, records, or any other type | | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Built-in support (no Jackson required)
|
||||
|
||||
The built-in path materialises JSON into the *natural* JSON value types
|
||||
(`String`, `Long`, `BigDecimal`, `Boolean`, `Map`, `List`). It is therefore type-safe only
|
||||
for the following declared property types:
|
||||
|
||||
- **`String`** — stored as raw JSON text.
|
||||
- **`List<String>`** and **`List<Long>`**.
|
||||
- **`Set<String>`** and **`Set<Long>`**.
|
||||
- **`Map<K, V>`** where:
|
||||
- the key `K` is `String` or an **enum**, and
|
||||
- the value `V` is `Object`, `String`, or a wildcard `?`.
|
||||
|
||||
So `Map<String,Object>`, `Map<String,String>`, `Map<Enum,Object>` and `Map<Enum,String>`
|
||||
are all built-in.
|
||||
|
||||
These mappings work across all supported storage types — `VARCHAR`, `CLOB`, `BLOB`, and
|
||||
Postgres `json` / `jsonb` — without `ebean-jackson-mapper`.
|
||||
|
||||
---
|
||||
|
||||
## Everything else → Jackson `ObjectMapper`
|
||||
|
||||
Any other `@DbJson` / `@DbJsonB` property routes to the Jackson `ObjectMapper` path, which
|
||||
requires `ebean-jackson-mapper`:
|
||||
|
||||
- **Typed collections** — `List`/`Set` whose element type is not `String` or `Long`
|
||||
(for example `List<Integer>`, `List<UUID>`, `List<LocalDate>`, `List<MyEnum>`, `List<MyPojo>`).
|
||||
- **Typed-value maps** — a `Map` value type other than `String`/`Object`
|
||||
(for example `Map<String,Integer>`, `Map<String,UUID>`, `Map<String,MyPojo>`).
|
||||
- **Non-`String`/non-enum map keys** — for example `Map<Integer,Object>`, `Map<UUID,String>`.
|
||||
- **POJOs, records, and any other custom type.**
|
||||
|
||||
> **Jackson marker annotation override:** if the **field or getter** carries a Jackson annotation
|
||||
> (anything meta-annotated with `com.fasterxml.jackson.annotation.JacksonAnnotation`), Ebean
|
||||
> uses the `ObjectMapper` path even when the type would otherwise be handled built-in.
|
||||
|
||||
---
|
||||
|
||||
## Adding `ebean-jackson-mapper`
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>${ebean.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
A Jackson `ObjectMapper` must be available (via `jackson-databind`). Ebean detects it and
|
||||
registers the mapper-based JSON support automatically.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **Enum map keys** are serialised using the enum `name()` (for example `ACTIVE`), not any
|
||||
`@DbEnumValue` mapping. Round-trips are correct; the DB value mapping is not applied to
|
||||
JSON keys.
|
||||
- **`@DbArray` alternative:** for typed *scalar* collections (`List`/`Set` of `Integer`,
|
||||
`Long`, `UUID`, `Double`, an enum, …) consider `@DbArray`, which maps to a native DB array
|
||||
(with a JSON fallback on platforms without array support) and supports more element types
|
||||
than built-in `@DbJson` collections.
|
||||
- The reason typed value/element collections need a real mapper is that the built-in path
|
||||
only produces natural JSON types — for example a JSON number always parses to `Long`, so a
|
||||
declared `List<Integer>` or `Map<String,Integer>` could not be populated safely without a
|
||||
type-aware mapper.
|
||||
|
||||
---
|
||||
|
||||
## Choosing
|
||||
|
||||
- Prefer the **built-in** mappings for the common cases (`String`, string/long lists and sets,
|
||||
object/string maps) to avoid pulling in Jackson.
|
||||
- Add **`ebean-jackson-mapper`** when you need rich POJO JSON columns or typed collections /
|
||||
typed-value maps.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Guide: Migrate JSON APIs from Jackson core to avaje-json-core
|
||||
|
||||
## Purpose
|
||||
|
||||
This guide covers the one-step cutover in Ebean from Jackson core JSON APIs to
|
||||
avaje-json-core APIs.
|
||||
|
||||
Use this when upgrading code that references:
|
||||
|
||||
- `com.fasterxml.jackson.core.JsonParser`
|
||||
- `com.fasterxml.jackson.core.JsonGenerator`
|
||||
- `com.fasterxml.jackson.core.JsonFactory`
|
||||
|
||||
The replacement types are:
|
||||
|
||||
- `io.avaje.json.JsonReader`
|
||||
- `io.avaje.json.JsonWriter`
|
||||
- `io.avaje.json.stream.JsonStream`
|
||||
|
||||
---
|
||||
|
||||
## Breaking API changes
|
||||
|
||||
| Previous API | New API |
|
||||
|---|---|
|
||||
| `JsonParser` | `JsonReader` |
|
||||
| `JsonGenerator` | `JsonWriter` |
|
||||
| `JsonFactory` | `JsonStream` |
|
||||
| `DatabaseBuilder.jsonFactory(...)` | `DatabaseBuilder.jsonStream(...)` |
|
||||
| `DatabaseConfig.getJsonFactory()/setJsonFactory(...)` | `DatabaseConfig.getJsonStream()/setJsonStream(...)` |
|
||||
|
||||
---
|
||||
|
||||
## Typical migration rewrites
|
||||
|
||||
### Parser and generator signatures
|
||||
|
||||
```java
|
||||
// before
|
||||
void read(JsonParser parser)
|
||||
void write(JsonGenerator generator)
|
||||
|
||||
// after
|
||||
void read(JsonReader parser)
|
||||
void write(JsonWriter generator)
|
||||
```
|
||||
|
||||
### Database configuration
|
||||
|
||||
```java
|
||||
// before
|
||||
Database.builder().jsonFactory(factory)
|
||||
|
||||
// after
|
||||
Database.builder().jsonStream(stream)
|
||||
```
|
||||
|
||||
### JSON utility calls
|
||||
|
||||
`EJson` and `JsonContext` APIs now operate on `JsonReader` and `JsonWriter` types.
|
||||
If your code was calling those APIs with Jackson core types, switch to avaje types.
|
||||
|
||||
---
|
||||
|
||||
## Dependency and module notes
|
||||
|
||||
- `ebean-core` no longer requires a direct `jackson-core` dependency for JSON
|
||||
parsing/writing.
|
||||
- `jackson-databind` remains optional for `ObjectMapper` compatibility paths.
|
||||
- `ebean-jackson-mapper` remains the compatibility bridge module for mapper-based
|
||||
integrations.
|
||||
|
||||
---
|
||||
|
||||
## Behavior notes to verify during upgrade
|
||||
|
||||
1. Parser token handling is now based on avaje `JsonReader.Token`.
|
||||
2. Scalar JSON reads (for example booleans, date-time, array scalar types) should
|
||||
be validated in your tests if you previously depended on Jackson token quirks.
|
||||
3. If your integration uses transient assoc-many JSON mapping with ObjectMapper,
|
||||
keep ObjectMapper wiring enabled.
|
||||
|
||||
---
|
||||
|
||||
## Validation checklist
|
||||
|
||||
1. Compile all modules that implement or consume `io.ebean.text.json` APIs.
|
||||
2. Run module tests that cover JSON scalar conversion and bean JSON round-trips.
|
||||
3. Confirm no remaining `com.fasterxml.jackson.core.*` imports in migrated code.
|
||||
4. Keep `ObjectMapper` compatibility tests if your project depends on mapper paths.
|
||||
|
||||
+5
-7
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
@@ -70,15 +70,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-json-core</artifactId>
|
||||
<version>${avaje-json-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
<!-- Jackson databind remains for ObjectMapper compatibility paths -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.annotation.*;
|
||||
import io.ebean.cache.ServerCachePlugin;
|
||||
import io.ebean.config.*;
|
||||
@@ -360,18 +360,18 @@ public interface DatabaseBuilder {
|
||||
DatabaseBuilder putServiceObject(Object configObject);
|
||||
|
||||
/**
|
||||
* Set the Jackson JsonFactory to use.
|
||||
* Set the JsonStream to use.
|
||||
* <p>
|
||||
* If not set a default implementation will be used.
|
||||
*/
|
||||
default DatabaseBuilder jsonFactory(JsonFactory jsonFactory) {
|
||||
return setJsonFactory(jsonFactory);
|
||||
default DatabaseBuilder jsonStream(JsonStream jsonStream) {
|
||||
return setJsonStream(jsonStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated migrate to {@link #jsonFactory(JsonFactory)}.
|
||||
* @deprecated migrate to {@link #jsonStream(JsonStream)}.
|
||||
*/
|
||||
DatabaseBuilder setJsonFactory(JsonFactory jsonFactory);
|
||||
DatabaseBuilder setJsonStream(JsonStream jsonStream);
|
||||
|
||||
/**
|
||||
* Set the JSON format to use for DateTime types.
|
||||
@@ -2254,11 +2254,11 @@ public interface DatabaseBuilder {
|
||||
boolean isAutoLoadModuleInfo();
|
||||
|
||||
/**
|
||||
* Return the Jackson JsonFactory to use.
|
||||
* Return the JsonStream to use.
|
||||
* <p>
|
||||
* If not set a default implementation will be used.
|
||||
*/
|
||||
JsonFactory getJsonFactory();
|
||||
JsonStream getJsonStream();
|
||||
|
||||
/**
|
||||
* Get the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
|
||||
|
||||
@@ -60,7 +60,8 @@ public class ClassLoadConfig {
|
||||
}
|
||||
|
||||
public boolean isJacksonCorePresent() {
|
||||
return isPresent("com.fasterxml.jackson.core.JsonParser");
|
||||
// Legacy method name retained for compatibility; now checks avaje JSON core.
|
||||
return isPresent("io.avaje.json.JsonReader");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,4 +159,3 @@ public class ClassLoadConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import io.avaje.config.Config;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.*;
|
||||
import io.ebean.annotation.MutationDetection;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
@@ -420,7 +420,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
* The default PersistenceContextScope used if one is not explicitly set on a query.
|
||||
*/
|
||||
private PersistenceContextScope persistenceContextScope = PersistenceContextScope.TRANSACTION;
|
||||
private JsonFactory jsonFactory;
|
||||
private JsonStream jsonStream;
|
||||
private boolean localTimeWithNanos;
|
||||
private boolean durationWithNanos;
|
||||
private int maxCallStack = 5;
|
||||
@@ -631,13 +631,13 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonFactory getJsonFactory() {
|
||||
return jsonFactory;
|
||||
public JsonStream getJsonStream() {
|
||||
return jsonStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseConfig setJsonFactory(JsonFactory jsonFactory) {
|
||||
this.jsonFactory = jsonFactory;
|
||||
public DatabaseConfig setJsonStream(JsonStream jsonStream) {
|
||||
this.jsonStream = jsonStream;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebean.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
@@ -32,12 +32,12 @@ public interface SpiJsonService extends BootstrapService {
|
||||
/**
|
||||
* Write the nested Map/List as json to the jsonGenerator.
|
||||
*/
|
||||
void write(Object object, JsonGenerator jsonGenerator) throws IOException;
|
||||
void write(Object object, JsonWriter jsonGenerator) throws IOException;
|
||||
|
||||
/**
|
||||
* Write the collection as json array to the jsonGenerator.
|
||||
*/
|
||||
void writeCollection(Collection<Object> collection, JsonGenerator jsonGenerator) throws IOException;
|
||||
void writeCollection(Collection<Object> collection, JsonWriter jsonGenerator) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map additionally specifying if the returned map should
|
||||
@@ -61,17 +61,17 @@ public interface SpiJsonService extends BootstrapService {
|
||||
Map<String, Object> parseObject(Reader reader) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser.
|
||||
* Parse the json and return as a Map taking a JsonReader.
|
||||
*/
|
||||
Map<String, Object> parseObject(JsonParser parser) throws IOException;
|
||||
Map<String, Object> parseObject(JsonReader parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser and a starting token.
|
||||
* Parse the json and return as a Map taking a JsonReader and a starting token.
|
||||
* <p>
|
||||
* Used when the first token is checked to see if the value is null prior to calling this.
|
||||
* </p>
|
||||
*/
|
||||
Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException;
|
||||
Map<String, Object> parseObject(JsonReader parser, Token token) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json and return as a modify aware List.
|
||||
@@ -89,14 +89,14 @@ public interface SpiJsonService extends BootstrapService {
|
||||
List<Object> parseList(Reader reader) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a JsonParser.
|
||||
* Parse the json and return as a List taking a JsonReader.
|
||||
*/
|
||||
List<Object> parseList(JsonParser parser) throws IOException;
|
||||
List<Object> parseList(JsonReader parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json returning as a List taking into account the current token.
|
||||
*/
|
||||
<T> List<T> parseList(JsonParser parser, JsonToken currentToken) throws IOException;
|
||||
<T> List<T> parseList(JsonReader parser, Token currentToken) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
@@ -111,7 +111,7 @@ public interface SpiJsonService extends BootstrapService {
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
Object parse(JsonParser parser) throws IOException;
|
||||
Object parse(JsonReader parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse the json returning a Set that might be modify aware.
|
||||
@@ -121,5 +121,5 @@ public interface SpiJsonService extends BootstrapService {
|
||||
/**
|
||||
* Parse the json returning as a Set taking into account the current token.
|
||||
*/
|
||||
<T> Set<T> parseSet(JsonParser parser, JsonToken currentToken) throws IOException;
|
||||
<T> Set<T> parseSet(JsonReader parser, Token currentToken) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package io.ebean.text.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.ebean.XBootstrapService;
|
||||
import io.ebean.service.SpiJsonService;
|
||||
|
||||
@@ -38,14 +37,14 @@ public class EJson {
|
||||
/**
|
||||
* Write the nested Map/List as json to the jsonGenerator.
|
||||
*/
|
||||
public static void write(Object object, JsonGenerator jsonGenerator) throws IOException {
|
||||
public static void write(Object object, io.avaje.json.JsonWriter jsonGenerator) throws IOException {
|
||||
plugin.write(object, jsonGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the collection as json array to the jsonGenerator.
|
||||
*/
|
||||
public static void writeCollection(Collection<Object> collection, JsonGenerator jsonGenerator) throws IOException {
|
||||
public static void writeCollection(Collection<Object> collection, io.avaje.json.JsonWriter jsonGenerator) throws IOException {
|
||||
plugin.writeCollection(collection, jsonGenerator);
|
||||
}
|
||||
|
||||
@@ -79,19 +78,19 @@ public class EJson {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser.
|
||||
* Parse the json and return as a Map taking a JsonReader.
|
||||
*/
|
||||
public static Map<String, Object> parseObject(JsonParser parser) throws IOException {
|
||||
public static Map<String, Object> parseObject(JsonReader parser) throws IOException {
|
||||
return plugin.parseObject(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser and a starting token.
|
||||
* Parse the json and return as a Map taking a JsonReader and a starting token.
|
||||
* <p>
|
||||
* Used when the first token is checked to see if the value is null prior to calling this.
|
||||
* </p>
|
||||
*/
|
||||
public static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
|
||||
public static Map<String, Object> parseObject(JsonReader parser, Token token) throws IOException {
|
||||
return plugin.parseObject(parser, token);
|
||||
}
|
||||
|
||||
@@ -117,16 +116,16 @@ public class EJson {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a JsonParser.
|
||||
* Parse the json and return as a List taking a JsonReader.
|
||||
*/
|
||||
public static List<Object> parseList(JsonParser parser) throws IOException {
|
||||
public static List<Object> parseList(JsonReader parser) throws IOException {
|
||||
return plugin.parseList(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json returning as a List taking into account the current token.
|
||||
*/
|
||||
public static <T> List<T> parseList(JsonParser parser, JsonToken currentToken) throws IOException {
|
||||
public static <T> List<T> parseList(JsonReader parser, Token currentToken) throws IOException {
|
||||
return plugin.parseList(parser, currentToken);
|
||||
}
|
||||
|
||||
@@ -147,7 +146,7 @@ public class EJson {
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
public static Object parse(JsonParser parser) throws IOException {
|
||||
public static Object parse(JsonReader parser) throws IOException {
|
||||
return plugin.parse(parser);
|
||||
}
|
||||
|
||||
@@ -161,7 +160,7 @@ public class EJson {
|
||||
/**
|
||||
* Parse the json returning as a Set taking into account the current token.
|
||||
*/
|
||||
public static <T> Set<T> parseSet(JsonParser parser, JsonToken currentToken) throws IOException {
|
||||
public static <T> Set<T> parseSet(JsonReader parser, Token currentToken) throws IOException {
|
||||
return plugin.parseSet(parser, currentToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.text.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
|
||||
/**
|
||||
@@ -25,9 +25,9 @@ public interface JsonBeanReader<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new reader taking the context from the existing one but using a new JsonParser.
|
||||
* Create a new reader taking the context from the existing one but using a new JsonReader.
|
||||
*/
|
||||
JsonBeanReader<T> forJson(JsonParser moreJson);
|
||||
JsonBeanReader<T> forJson(JsonReader moreJson);
|
||||
|
||||
/**
|
||||
* Add a bean explicitly to the persistence context.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebean.text.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.plugin.BeanType;
|
||||
|
||||
@@ -49,14 +48,14 @@ public interface JsonContext {
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
<T> T toBean(Class<T> cls, JsonParser parser) throws JsonIOException;
|
||||
<T> T toBean(Class<T> cls, JsonReader parser) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json parser input into a Bean of a specific type additionally using JsonReadOptions..
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
<T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
|
||||
<T> T toBean(Class<T> cls, JsonReader parser, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Read json parser input into a given Bean. <br>
|
||||
@@ -65,19 +64,19 @@ public interface JsonContext {
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
<T> void toBean(T target, JsonParser parser) throws JsonIOException;
|
||||
<T> void toBean(T target, JsonReader parser) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Read json parser input into a given Bean additionally using JsonReadOptions.<br>
|
||||
* See {@link #toBean(Class, JsonParser)} for details modified.
|
||||
* See {@link #toBean(Class, JsonReader)} for details modified.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
<T> void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException;
|
||||
<T> void toBean(T target, JsonReader parser, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Read json reader input into a given Bean.<br>
|
||||
* See {@link #toBean(Class, JsonParser)} for details
|
||||
* See {@link #toBean(Class, JsonReader)} for details
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
@@ -85,7 +84,7 @@ public interface JsonContext {
|
||||
|
||||
/**
|
||||
* Read json reader input into a given Bean additionally using JsonReadOptions.<br>
|
||||
* See {@link #toBean(Class, JsonParser)} for details modified.
|
||||
* See {@link #toBean(Class, JsonReader)} for details modified.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
@@ -93,7 +92,7 @@ public interface JsonContext {
|
||||
|
||||
/**
|
||||
* Read json string input into a given Bean.<br>
|
||||
* See {@link #toBean(Class, JsonParser)} for details
|
||||
* See {@link #toBean(Class, JsonReader)} for details
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
@@ -101,7 +100,7 @@ public interface JsonContext {
|
||||
|
||||
/**
|
||||
* Read json string input into a given Bean additionally using JsonReadOptions.<br>
|
||||
* See {@link #toBean(Class, JsonParser)} for details
|
||||
* See {@link #toBean(Class, JsonReader)} for details
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
@@ -113,7 +112,7 @@ public interface JsonContext {
|
||||
* Note that JsonOption provides an option for setting a persistence context and also enabling further lazy loading. Further lazy
|
||||
* loading requires a persistence context so if that is set on then a persistence context is created if there is not one set.
|
||||
*/
|
||||
<T> JsonBeanReader<T> createBeanReader(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
|
||||
<T> JsonBeanReader<T> createBeanReader(Class<T> cls, JsonReader parser, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Create and return a new bean reading for the bean type given the JSON options and source.
|
||||
@@ -122,7 +121,7 @@ public interface JsonContext {
|
||||
* further lazy loading. Further lazy loading requires a persistence context so if that is set
|
||||
* on then a persistence context is created if there is not one set.
|
||||
*/
|
||||
<T> JsonBeanReader<T> createBeanReader(BeanType<T> beanType, JsonParser parser, JsonReadOptions options) throws JsonIOException;
|
||||
<T> JsonBeanReader<T> createBeanReader(BeanType<T> beanType, JsonReader parser, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json string input into a list of beans of a specific type.
|
||||
@@ -157,14 +156,14 @@ public interface JsonContext {
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
<T> List<T> toList(Class<T> cls, JsonParser json) throws JsonIOException;
|
||||
<T> List<T> toList(Class<T> cls, JsonReader json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json parser input into a list of beans of a specific type additionally using JsonReadOptions.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
<T> List<T> toList(Class<T> cls, JsonParser json, JsonReadOptions options) throws JsonIOException;
|
||||
<T> List<T> toList(Class<T> cls, JsonReader json, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Use the genericType to determine if this should be converted into a List or
|
||||
@@ -188,7 +187,7 @@ public interface JsonContext {
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
Object toObject(Type genericType, JsonParser jsonParser) throws JsonIOException;
|
||||
Object toObject(Type genericType, JsonReader jsonParser) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Return the bean or collection as JSON string.
|
||||
@@ -212,11 +211,11 @@ public interface JsonContext {
|
||||
void toJson(Object value, Writer writer) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection to the JsonGenerator.
|
||||
* Write the bean or collection to the JsonWriter.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
void toJson(Object value, JsonGenerator generator) throws JsonIOException;
|
||||
void toJson(Object value, io.avaje.json.JsonWriter generator) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Return the bean or collection as JSON string using FetchPath.
|
||||
@@ -231,15 +230,15 @@ public interface JsonContext {
|
||||
void toJson(Object value, Writer writer, FetchPath fetchPath) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection to the JsonGenerator using the FetchPath.
|
||||
* Write the bean or collection to the JsonWriter using the FetchPath.
|
||||
*/
|
||||
void toJson(Object value, JsonGenerator generator, FetchPath fetchPath) throws JsonIOException;
|
||||
void toJson(Object value, io.avaje.json.JsonWriter generator, FetchPath fetchPath) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* Write json to the JsonGenerator using the JsonWriteOptions.
|
||||
* Write json to the JsonWriter using the JsonWriteOptions.
|
||||
*/
|
||||
void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException;
|
||||
void toJson(Object value, io.avaje.json.JsonWriter generator, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
@@ -264,27 +263,27 @@ public interface JsonContext {
|
||||
boolean isSupportedType(Type genericType);
|
||||
|
||||
/**
|
||||
* Create and return a new JsonGenerator for the given writer.
|
||||
* Create and return a new JsonWriter for the given writer.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
JsonGenerator createGenerator(Writer writer) throws JsonIOException;
|
||||
io.avaje.json.JsonWriter createGenerator(Writer writer) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Create and return a new JsonParser for the given reader.
|
||||
* Create and return a new JsonReader for the given reader.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
JsonParser createParser(Reader reader) throws JsonIOException;
|
||||
JsonReader createParser(Reader reader) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write a scalar types known to Ebean to Jackson.
|
||||
* Write scalar types known to Ebean to JsonWriter.
|
||||
* <p>
|
||||
* Ebean has built in support for java8 and Joda types as well as the other
|
||||
* standard JDK types like URI, URL, UUID etc. This is a fast simple way to
|
||||
* write any of those types to Jackson.
|
||||
* write any of those types.
|
||||
* </p>
|
||||
*/
|
||||
void writeScalar(JsonGenerator generator, Object scalarValue) throws IOException;
|
||||
void writeScalar(io.avaje.json.JsonWriter generator, Object scalarValue) throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package io.ebean.text.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Wraps an underlying JsonGenerator taking into account null suppression and exposing isIncludeEmpty() etc.
|
||||
* Wraps an underlying JsonWriter taking into account null suppression and exposing isIncludeEmpty() etc.
|
||||
*/
|
||||
public interface JsonWriter {
|
||||
|
||||
/**
|
||||
* Return the Jackson core JsonGenerator.
|
||||
* Return the underlying JsonWriter.
|
||||
*/
|
||||
JsonGenerator gen();
|
||||
io.avaje.json.JsonWriter gen();
|
||||
|
||||
/**
|
||||
* Return true if null values should be included in JSON output.
|
||||
|
||||
@@ -8,6 +8,7 @@ module io.ebean.api {
|
||||
|
||||
requires transitive java.sql;
|
||||
requires transitive io.avaje.config;
|
||||
requires transitive io.avaje.json;
|
||||
requires transitive org.jspecify;
|
||||
requires transitive jakarta.persistence.api;
|
||||
requires transitive io.ebean.annotation;
|
||||
@@ -16,7 +17,6 @@ module io.ebean.api {
|
||||
|
||||
requires static org.slf4j;
|
||||
requires static io.ebean.types;
|
||||
requires static com.fasterxml.jackson.core;
|
||||
requires static com.fasterxml.jackson.databind;
|
||||
|
||||
exports io.ebean;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-bench</artifactId>
|
||||
|
||||
+28
-28
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -89,25 +89,25 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -125,13 +125,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -155,37 +155,37 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-spring-txn</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- platforms -->
|
||||
@@ -193,91 +193,91 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-clickhouse</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-db2</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-h2</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-hana</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mariadb</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mysql</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-nuodb</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-oracle</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-pgvector</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-pgvector-types</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlite</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlserver</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
<artifactId>ebean-core-json</artifactId>
|
||||
<name>ebean-core-json</name>
|
||||
@@ -16,15 +16,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-json-core</artifactId>
|
||||
<version>${avaje-json-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -1,190 +1,170 @@
|
||||
package io.ebeaninternal.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.avaje.json.mapper.JsonMapper;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.service.SpiJsonService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility that converts between JSON content and simple java Maps/Lists.
|
||||
* <p>
|
||||
* Backed by avaje {@link JsonMapper} using {@link EbeanJsonAdapter} which
|
||||
* preserves Ebean's modify-aware collection and number semantics.
|
||||
*/
|
||||
public final class DJsonService implements SpiJsonService {
|
||||
|
||||
/**
|
||||
* Write the nested Map/List as json.
|
||||
*/
|
||||
private static final JsonStream JSON_STREAM = JsonStream.builder().build();
|
||||
private static final JsonMapper MAPPER = JsonMapper.builder().jsonStream(JSON_STREAM).build();
|
||||
|
||||
private static final JsonMapper.Type<Object> PLAIN = MAPPER.type(EbeanJsonAdapter.PLAIN);
|
||||
private static final JsonMapper.Type<Object> MODIFY_AWARE = MAPPER.type(EbeanJsonAdapter.MODIFY_AWARE);
|
||||
|
||||
private static JsonMapper.Type<Object> type(boolean modifyAware) {
|
||||
return modifyAware ? MODIFY_AWARE : PLAIN;
|
||||
}
|
||||
|
||||
private static boolean blank(String content) {
|
||||
return content == null || content.trim().isEmpty();
|
||||
}
|
||||
|
||||
private static String readAll(Reader reader) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
char[] buffer = new char[2048];
|
||||
int len;
|
||||
while ((len = reader.read(buffer)) != -1) {
|
||||
builder.append(buffer, 0, len);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String write(Object object) throws IOException {
|
||||
return EJsonWriter.write(object);
|
||||
StringWriter writer = new StringWriter();
|
||||
write(object, writer);
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the nested Map/List as json to the writer.
|
||||
*/
|
||||
@Override
|
||||
public void write(Object object, Writer writer) throws IOException {
|
||||
EJsonWriter.write(object, writer);
|
||||
JsonWriter jsonWriter = JSON_STREAM.writer(writer);
|
||||
jsonWriter.serializeNulls(true);
|
||||
PLAIN.toJson(object, jsonWriter);
|
||||
jsonWriter.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the nested Map/List as json to the jsonGenerator.
|
||||
*/
|
||||
@Override
|
||||
public void write(Object object, JsonGenerator jsonGenerator) throws IOException {
|
||||
EJsonWriter.write(object, jsonGenerator);
|
||||
public void write(Object object, JsonWriter jsonWriter) throws IOException {
|
||||
PLAIN.toJson(object, jsonWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the collection as json array to the jsonGenerator.
|
||||
*/
|
||||
@Override
|
||||
public void writeCollection(Collection<Object> collection, JsonGenerator jsonGenerator) throws IOException {
|
||||
EJsonWriter.writeCollection(collection, jsonGenerator);
|
||||
public void writeCollection(Collection<Object> collection, JsonWriter jsonWriter) throws IOException {
|
||||
EbeanJsonAdapter.writeCollection(jsonWriter, collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map additionally specifying if the returned map should be modify
|
||||
* aware meaning that it can detect when it has been modified.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException {
|
||||
return EJsonReader.parseObject(json, modifyAware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseObject(String json) throws IOException {
|
||||
return EJsonReader.parseObject(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a reader.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseObject(Reader reader, boolean modifyAware) throws IOException {
|
||||
return EJsonReader.parseObject(reader, modifyAware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a reader.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseObject(Reader reader) throws IOException {
|
||||
return EJsonReader.parseObject(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseObject(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parseObject(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser and a starting token.
|
||||
*
|
||||
* <p>Used when the first token is checked to see if the value is null prior to calling this.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
|
||||
return EJsonReader.parseObject(parser, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a modify aware List.
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> parseList(String json, boolean modifyAware) throws IOException {
|
||||
return EJsonReader.parseList(json, modifyAware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List.
|
||||
*/
|
||||
@Override
|
||||
public List<Object> parseList(String json) throws IOException {
|
||||
return EJsonReader.parseList(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a Reader.
|
||||
*/
|
||||
@Override
|
||||
public List<Object> parseList(Reader reader) throws IOException {
|
||||
return EJsonReader.parseList(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a JsonParser.
|
||||
*/
|
||||
@Override
|
||||
public List<Object> parseList(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parseList(parser, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json returning as a List taking into account the current token.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> parseList(JsonParser parser, JsonToken currentToken) throws IOException {
|
||||
return (List<T>) EJsonReader.parse(parser, currentToken, false);
|
||||
public Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException {
|
||||
return blank(json) ? null : (Map<String, Object>) type(modifyAware).fromJson(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parseObject(String json) throws IOException {
|
||||
return parseObject(json, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parseObject(Reader reader, boolean modifyAware) throws IOException {
|
||||
return parseObject(readAll(reader), modifyAware);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parseObject(Reader reader) throws IOException {
|
||||
return parseObject(reader, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> parseObject(JsonReader parser) throws IOException {
|
||||
return (Map<String, Object>) PLAIN.fromJson(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> parseObject(JsonReader parser, Token token) throws IOException {
|
||||
return (Map<String, Object>) EbeanJsonAdapter.read(parser, token, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> parseList(String json, boolean modifyAware) throws IOException {
|
||||
return blank(json) ? null : (List<T>) type(modifyAware).fromJson(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> parseList(String json) throws IOException {
|
||||
return (List<Object>) parseList(json, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> parseList(Reader reader) throws IOException {
|
||||
return parseList(readAll(reader));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> parseList(JsonReader parser) throws IOException {
|
||||
return (List<Object>) PLAIN.fromJson(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> parseList(JsonReader parser, Token currentToken) throws IOException {
|
||||
return (List<T>) EbeanJsonAdapter.read(parser, currentToken, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
@Override
|
||||
public Object parse(String json) throws IOException {
|
||||
return EJsonReader.parse(json);
|
||||
return blank(json) ? null : PLAIN.fromJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
@Override
|
||||
public Object parse(Reader reader) throws IOException {
|
||||
return EJsonReader.parse(reader);
|
||||
return parse(readAll(reader));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
@Override
|
||||
public Object parse(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parse(parser);
|
||||
public Object parse(JsonReader parser) throws IOException {
|
||||
return PLAIN.fromJson(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json returning a Set that might be modify aware.
|
||||
*/
|
||||
@Override
|
||||
public <T> Set<T> parseSet(String json, boolean modifyAware) throws IOException {
|
||||
List<T> list = parseList(json, modifyAware);
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (modifyAware) {
|
||||
return ((ModifyAwareList<T>) list).asSet();
|
||||
} else {
|
||||
return new LinkedHashSet<>(list);
|
||||
}
|
||||
return new LinkedHashSet<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json returning as a Set taking into account the current token.
|
||||
*/
|
||||
@Override
|
||||
public <T> Set<T> parseSet(JsonParser parser, JsonToken currentToken) throws IOException {
|
||||
public <T> Set<T> parseSet(JsonReader parser, Token currentToken) throws IOException {
|
||||
return new LinkedHashSet<>(parseList(parser, currentToken));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
package io.ebeaninternal.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.ModifyAwareType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
|
||||
final class EJsonReader {
|
||||
|
||||
static final JsonFactory json = new JsonFactory();
|
||||
private final JsonParser parser;
|
||||
private final boolean modifyAware;
|
||||
private final ModifyAwareFlag modifyAwareOwner;
|
||||
private int depth;
|
||||
private Stack stack;
|
||||
private Context currentContext;
|
||||
|
||||
EJsonReader(JsonParser parser, boolean modifyAware) {
|
||||
this.parser = parser;
|
||||
this.modifyAware = modifyAware;
|
||||
this.modifyAwareOwner = modifyAware ? new ModifyAwareFlag() : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(String json, boolean modifyAware) throws IOException {
|
||||
return (Map<String, Object>) parse(json, modifyAware);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(String json) throws IOException {
|
||||
return (Map<String, Object>) parse(json);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(Reader reader) throws IOException {
|
||||
return (Map<String, Object>) parse(reader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(Reader reader, boolean modifyAware) throws IOException {
|
||||
return (Map<String, Object>) parse(reader, modifyAware);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(JsonParser parser) throws IOException {
|
||||
return (Map<String, Object>) parse(parser);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
|
||||
return (Map<String, Object>) parse(parser, token, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> List<T> parseList(String json, boolean modifyAware) throws IOException {
|
||||
return (List<T>) parse(json, modifyAware);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(String json) throws IOException {
|
||||
return (List<Object>) parse(json);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(Reader reader) throws IOException {
|
||||
return (List<Object>) parse(reader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(JsonParser parser, boolean modifyAware) throws IOException {
|
||||
return (List<Object>) parse(parser, modifyAware);
|
||||
}
|
||||
|
||||
static Object parse(String json) throws IOException {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(new StringReader(json));
|
||||
}
|
||||
|
||||
static Object parse(String json, boolean modifyAware) throws IOException {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(new StringReader(json), modifyAware);
|
||||
}
|
||||
|
||||
static Object parse(Reader reader) throws IOException {
|
||||
return parse(json.createParser(reader));
|
||||
}
|
||||
|
||||
static Object parse(Reader reader, boolean modifyAware) throws IOException {
|
||||
return parse(json.createParser(reader), modifyAware);
|
||||
}
|
||||
|
||||
static Object parse(JsonParser parser) throws IOException {
|
||||
return parse(parser, null, false);
|
||||
}
|
||||
|
||||
static Object parse(JsonParser parser, boolean modifyAware) throws IOException {
|
||||
return parse(parser, null, modifyAware);
|
||||
}
|
||||
|
||||
static Object parse(JsonParser parser, JsonToken token, boolean modifyAware) throws IOException {
|
||||
return new EJsonReader(parser, modifyAware).parseJson(token);
|
||||
}
|
||||
|
||||
private void startArray() {
|
||||
depth++;
|
||||
stack.push(currentContext);
|
||||
currentContext = modifyAware ? new ArrayContext(modifyAwareOwner) : new ArrayContext();
|
||||
}
|
||||
|
||||
private void startObject() {
|
||||
depth++;
|
||||
stack.push(currentContext);
|
||||
currentContext = modifyAware ? new ObjectContext(modifyAwareOwner) : new ObjectContext();
|
||||
}
|
||||
|
||||
private void endArray() {
|
||||
end();
|
||||
}
|
||||
|
||||
private void endObject() {
|
||||
end();
|
||||
}
|
||||
|
||||
private void end() {
|
||||
depth--;
|
||||
if (!stack.isEmpty()) {
|
||||
currentContext = stack.pop(currentContext);
|
||||
}
|
||||
if (modifyAwareOwner != null) {
|
||||
modifyAwareOwner.setMarkedDirty(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setValue(Object value) {
|
||||
currentContext.setValue(value);
|
||||
}
|
||||
|
||||
private void setValueNull() {
|
||||
currentContext.setValueNull();
|
||||
}
|
||||
|
||||
private Object parseJson(JsonToken token) throws IOException {
|
||||
|
||||
if (token == null) {
|
||||
token = parser.nextToken();
|
||||
// if it is a simple value just return it
|
||||
switch (token) {
|
||||
case VALUE_NULL:
|
||||
return null;
|
||||
case VALUE_FALSE:
|
||||
return Boolean.FALSE;
|
||||
case VALUE_TRUE:
|
||||
return Boolean.TRUE;
|
||||
case VALUE_STRING:
|
||||
return parser.getText();
|
||||
case VALUE_NUMBER_INT:
|
||||
return parser.getLongValue();
|
||||
case VALUE_NUMBER_FLOAT:
|
||||
return parser.getDecimalValue();
|
||||
}
|
||||
}
|
||||
|
||||
// it is a object or array, process the first JsonToken
|
||||
stack = new Stack();
|
||||
processJsonToken(token);
|
||||
|
||||
// process the rest of the object or array
|
||||
while (depth > 0) {
|
||||
token = parser.nextToken();
|
||||
processJsonToken(token);
|
||||
}
|
||||
|
||||
return currentContext.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the JsonToken for objects and arrays.
|
||||
*/
|
||||
private void processJsonToken(JsonToken token) throws IOException {
|
||||
switch (token) {
|
||||
case START_ARRAY:
|
||||
startArray();
|
||||
break;
|
||||
|
||||
case START_OBJECT:
|
||||
startObject();
|
||||
break;
|
||||
|
||||
case FIELD_NAME:
|
||||
currentContext.setKey(parser.getCurrentName());
|
||||
break;
|
||||
|
||||
case VALUE_STRING:
|
||||
setValue(parser.getValueAsString());
|
||||
break;
|
||||
|
||||
case VALUE_NUMBER_INT:
|
||||
setValue(parser.getLongValue());
|
||||
break;
|
||||
|
||||
case VALUE_NUMBER_FLOAT:
|
||||
setValue(parser.getDecimalValue());
|
||||
break;
|
||||
|
||||
case VALUE_TRUE:
|
||||
setValue(Boolean.TRUE);
|
||||
break;
|
||||
|
||||
case VALUE_FALSE:
|
||||
setValue(Boolean.FALSE);
|
||||
break;
|
||||
|
||||
case VALUE_NULL:
|
||||
setValueNull();
|
||||
break;
|
||||
|
||||
case END_OBJECT:
|
||||
endObject();
|
||||
break;
|
||||
|
||||
case END_ARRAY:
|
||||
endArray();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Stack {
|
||||
|
||||
private Context head;
|
||||
|
||||
private void push(Context context) {
|
||||
if (context != null) {
|
||||
context.next = head;
|
||||
head = context;
|
||||
}
|
||||
}
|
||||
|
||||
private Context pop(Context endingContext) {
|
||||
if (head == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
Context temp = head;
|
||||
head = head.next;
|
||||
temp.popContext(endingContext);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return head == null;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class Context {
|
||||
Context next;
|
||||
|
||||
abstract void popContext(Context temp);
|
||||
|
||||
abstract Object getValue();
|
||||
|
||||
abstract void setValue(Object value);
|
||||
|
||||
abstract void setKey(String key);
|
||||
|
||||
abstract void setValueNull();
|
||||
}
|
||||
|
||||
private static class ObjectContext extends Context {
|
||||
|
||||
private final Map<String, Object> map;
|
||||
|
||||
private String key;
|
||||
|
||||
ObjectContext() {
|
||||
map = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
ObjectContext(ModifyAwareType owner) {
|
||||
map = new ModifyAwareMap<>(owner, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popContext(Context temp) {
|
||||
setValue(temp.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getValue() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setValue(Object value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setValueNull() {
|
||||
map.put(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrayContext extends Context {
|
||||
|
||||
private final List<Object> values;
|
||||
|
||||
ArrayContext() {
|
||||
values = new ArrayList<>();
|
||||
}
|
||||
|
||||
ArrayContext(ModifyAwareType owner) {
|
||||
values = new ModifyAwareList<>(owner, new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popContext(Context temp) {
|
||||
values.add(temp.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getValue() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setValue(Object value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setValueNull() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
void setKey(String key) {
|
||||
// not expected
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
package io.ebeaninternal.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
final class EJsonWriter {
|
||||
|
||||
/**
|
||||
* Base jsonFactory implementation used when it is not passed in.
|
||||
*/
|
||||
static final JsonFactory jsonFactory = new JsonFactory();
|
||||
private final JsonGenerator jsonGenerator;
|
||||
|
||||
private EJsonWriter(JsonGenerator jsonGenerator) {
|
||||
this.jsonGenerator = jsonGenerator;
|
||||
}
|
||||
|
||||
static String write(Object object) throws IOException {
|
||||
StringWriter writer = new StringWriter(200);
|
||||
write(object, writer).close();
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
static JsonGenerator write(Object object, Writer writer) throws IOException {
|
||||
JsonGenerator generator = jsonFactory.createGenerator(writer);
|
||||
write(object, generator);
|
||||
generator.flush();
|
||||
return generator;
|
||||
}
|
||||
|
||||
static void write(Object object, JsonGenerator jsonGenerator) {
|
||||
new EJsonWriter(jsonGenerator).writeJson(object);
|
||||
}
|
||||
|
||||
static void writeCollection(Collection<Object> collection, JsonGenerator jsonGenerator) throws IOException {
|
||||
new EJsonWriter(jsonGenerator).writeCollection(null, collection);
|
||||
}
|
||||
|
||||
private void writeJson(Object object) {
|
||||
writeJson(null, object);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void writeJson(String name, Object object) {
|
||||
try {
|
||||
if (object == null) {
|
||||
writeNull(name);
|
||||
|
||||
} else if (object instanceof Number) {
|
||||
writeNumber(name, (Number) object);
|
||||
|
||||
} else if (object instanceof String) {
|
||||
writeString(name, (String) object);
|
||||
|
||||
} else if (object instanceof Map) {
|
||||
writeMap(name, (Map<Object, Object>) object);
|
||||
|
||||
} else if (object instanceof Collection) {
|
||||
writeCollection(name, (Collection<Object>) object);
|
||||
|
||||
} else if (object instanceof Boolean) {
|
||||
writeBoolean(name, (Boolean) object);
|
||||
|
||||
} else if (object instanceof Date) {
|
||||
writeDate(name, (Date) object);
|
||||
|
||||
} else if (object instanceof Map.Entry<?, ?>) {
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
|
||||
writeJson(entry.getKey().toString(), entry.getValue());
|
||||
|
||||
} else {
|
||||
writeString(name, object.toString());
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBoolean(String name, Boolean object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeBoolean(object);
|
||||
} else {
|
||||
jsonGenerator.writeBooleanField(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDate(String name, Date object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber(object.getTime());
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, object.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeNumber(String name, Number object) throws IOException {
|
||||
|
||||
if (object instanceof Long) {
|
||||
writeLong(name, object);
|
||||
|
||||
} else if (object instanceof Integer) {
|
||||
writeInteger(name, object);
|
||||
|
||||
} else if (object instanceof Double) {
|
||||
writeDouble(name, object);
|
||||
|
||||
} else if (object instanceof BigDecimal) {
|
||||
writeBigDecimal(name, object);
|
||||
|
||||
} else if (object instanceof BigInteger) {
|
||||
writeBigInteger(name, object);
|
||||
|
||||
} else {
|
||||
writeGeneralNumber(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeGeneralNumber(String name, Number object) throws IOException {
|
||||
writeBigDecimal(name, new BigDecimal(object.toString()));
|
||||
}
|
||||
|
||||
private void writeBigDecimal(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((BigDecimal) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (BigDecimal) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBigInteger(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((BigInteger) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, object.longValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDouble(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((Double) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (Double) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLong(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((Long) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (Long) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInteger(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((Integer) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (Integer) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeNull(String name) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNull();
|
||||
} else {
|
||||
jsonGenerator.writeNullField(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeString(String name, String object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeString(object);
|
||||
} else {
|
||||
jsonGenerator.writeStringField(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCollection(String name, Collection<Object> collection) throws IOException {
|
||||
if (name != null) {
|
||||
jsonGenerator.writeFieldName(name);
|
||||
}
|
||||
jsonGenerator.writeStartArray();
|
||||
for (Object object : collection) {
|
||||
writeJson(null, object);
|
||||
}
|
||||
jsonGenerator.writeEndArray();
|
||||
}
|
||||
|
||||
private void writeMap(String name, Map<Object, Object> map) throws IOException {
|
||||
|
||||
if (name != null) {
|
||||
jsonGenerator.writeFieldName(name);
|
||||
}
|
||||
jsonGenerator.writeStartObject();
|
||||
Set<Entry<Object, Object>> entrySet = map.entrySet();
|
||||
for (Entry<Object, Object> entry : entrySet) {
|
||||
writeJson(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
jsonGenerator.writeEndObject();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package io.ebeaninternal.json;
|
||||
|
||||
import io.avaje.json.JsonAdapter;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.ModifyAwareType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Ebean specific {@link JsonAdapter} that materializes JSON into plain Java
|
||||
* Map/List/scalar values - optionally wrapped in modify-aware collections so
|
||||
* that mutations after load are tracked as dirty.
|
||||
* <p>
|
||||
* This consolidates the prior EJsonReader/EJsonWriter behavior into a single
|
||||
* adapter that plugs into avaje {@code JsonMapper}.
|
||||
*/
|
||||
final class EbeanJsonAdapter implements JsonAdapter<Object> {
|
||||
|
||||
static final EbeanJsonAdapter PLAIN = new EbeanJsonAdapter(false);
|
||||
static final EbeanJsonAdapter MODIFY_AWARE = new EbeanJsonAdapter(true);
|
||||
|
||||
private static final JsonStream JSON_STREAM = JsonStream.builder().build();
|
||||
|
||||
private final boolean modifyAware;
|
||||
|
||||
private EbeanJsonAdapter(boolean modifyAware) {
|
||||
this.modifyAware = modifyAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromJson(JsonReader reader) {
|
||||
return read(reader, null, modifyAware);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(JsonWriter writer, Object value) {
|
||||
write(writer, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a value honoring an explicitly supplied current token (or current token when null).
|
||||
*/
|
||||
static Object read(JsonReader parser, Token token, boolean modifyAware) {
|
||||
ModifyAwareType owner = modifyAware ? new ModifyAwareFlag() : null;
|
||||
Token effectiveToken = token == null ? parser.currentToken() : token;
|
||||
Object value;
|
||||
if (effectiveToken == null) {
|
||||
value = parseRawJson(parser.readRaw(), owner);
|
||||
} else {
|
||||
value = parseValue(parser, effectiveToken, owner);
|
||||
}
|
||||
if (owner != null) {
|
||||
owner.setMarkedDirty(false);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Object parseValue(JsonReader parser, Token token, ModifyAwareType owner) {
|
||||
if (token == null) {
|
||||
token = parser.currentToken();
|
||||
if (token == null) {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
return parseRawJson(parser.readRaw(), owner);
|
||||
}
|
||||
}
|
||||
switch (token) {
|
||||
case BEGIN_OBJECT:
|
||||
return parseObjectValue(parser, owner);
|
||||
case BEGIN_ARRAY:
|
||||
return parseArrayValue(parser, owner);
|
||||
case NUMBER:
|
||||
BigDecimal value = parser.readDecimal();
|
||||
return value.scale() <= 0 ? value.longValue() : value;
|
||||
case STRING:
|
||||
return parser.readString();
|
||||
case BOOLEAN:
|
||||
return parser.readBoolean();
|
||||
case NULL:
|
||||
parser.isNullValue();
|
||||
return null;
|
||||
default:
|
||||
return parseRawJson(parser.readRaw(), owner);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object parseRawJson(String json, ModifyAwareType owner) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
String content = json.trim();
|
||||
if (content.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try (JsonReader parser = JSON_STREAM.reader(content)) {
|
||||
return parseValue(parser, parser.currentToken(), owner);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> parseObjectValue(JsonReader parser, ModifyAwareType owner) {
|
||||
Map<String, Object> map = owner == null
|
||||
? new LinkedHashMap<>()
|
||||
: new ModifyAwareMap<>(owner, new LinkedHashMap<>());
|
||||
|
||||
parser.beginObject();
|
||||
while (parser.hasNextField()) {
|
||||
String fieldName = parser.nextField();
|
||||
map.put(fieldName, parseValue(parser, parser.currentToken(), owner));
|
||||
}
|
||||
parser.endObject();
|
||||
return map;
|
||||
}
|
||||
|
||||
private static List<Object> parseArrayValue(JsonReader parser, ModifyAwareType owner) {
|
||||
List<Object> list = owner == null
|
||||
? new ArrayList<>()
|
||||
: new ModifyAwareList<>(owner, new ArrayList<>());
|
||||
|
||||
parser.beginArray();
|
||||
while (parser.hasNextElement()) {
|
||||
list.add(parseValue(parser, parser.currentToken(), owner));
|
||||
}
|
||||
parser.endArray();
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the value to an existing JsonWriter (used for the raw stream paths).
|
||||
*/
|
||||
static void write(JsonWriter jsonWriter, Object object) {
|
||||
if (object == null) {
|
||||
jsonWriter.nullValue();
|
||||
} else if (object instanceof String) {
|
||||
jsonWriter.value((String) object);
|
||||
} else if (object instanceof Integer) {
|
||||
jsonWriter.value((Integer) object);
|
||||
} else if (object instanceof Long) {
|
||||
jsonWriter.value((Long) object);
|
||||
} else if (object instanceof Double) {
|
||||
jsonWriter.value((Double) object);
|
||||
} else if (object instanceof Float) {
|
||||
jsonWriter.value((Float) object);
|
||||
} else if (object instanceof BigDecimal) {
|
||||
jsonWriter.value((BigDecimal) object);
|
||||
} else if (object instanceof Boolean) {
|
||||
jsonWriter.value((Boolean) object);
|
||||
} else if (object instanceof Map<?, ?>) {
|
||||
writeMap(jsonWriter, (Map<?, ?>) object);
|
||||
} else if (object instanceof Collection<?>) {
|
||||
writeCollection(jsonWriter, (Collection<?>) object);
|
||||
} else {
|
||||
jsonWriter.value(object.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeMap(JsonWriter jsonWriter, Map<?, ?> map) {
|
||||
jsonWriter.beginObject();
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
jsonWriter.name((String) entry.getKey());
|
||||
write(jsonWriter, entry.getValue());
|
||||
}
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
|
||||
static void writeCollection(JsonWriter jsonWriter, Collection<?> collection) {
|
||||
jsonWriter.beginArray();
|
||||
for (Object element : collection) {
|
||||
write(jsonWriter, element);
|
||||
}
|
||||
jsonWriter.endArray();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
module io.ebean.core.json {
|
||||
|
||||
requires io.ebean.api;
|
||||
|
||||
requires transitive com.fasterxml.jackson.core;
|
||||
requires transitive io.avaje.json;
|
||||
exports io.ebeaninternal.json to io.ebean.test, io.ebean.core;
|
||||
|
||||
provides io.ebean.service.BootstrapService with io.ebeaninternal.json.DJsonService;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,14 +16,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-json-core</artifactId>
|
||||
<version>${avaje-json-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope for Postgres JSON/JSONB support -->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.core.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.text.StringFormatter;
|
||||
import io.ebean.text.StringParser;
|
||||
|
||||
@@ -177,13 +177,44 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
|
||||
void writeData(DataOutput dataOutput, T value) throws IOException;
|
||||
|
||||
/**
|
||||
* Read the value from JsonParser.
|
||||
* Read the value from JsonReader.
|
||||
*/
|
||||
T jsonRead(JsonParser parser) throws IOException;
|
||||
default T jsonRead(JsonReader parser) throws IOException {
|
||||
JsonReader.Token token = parser.currentToken();
|
||||
if (token == JsonReader.Token.NULL) {
|
||||
parser.isNullValue();
|
||||
return null;
|
||||
}
|
||||
if (token == JsonReader.Token.STRING) {
|
||||
return parse(parser.readString());
|
||||
}
|
||||
return parse(parser.readRaw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the value to the JsonGenerator.
|
||||
* Write the value to the JsonWriter.
|
||||
*/
|
||||
void jsonWrite(JsonGenerator writer, T value) throws IOException;
|
||||
default void jsonWrite(JsonWriter writer, T value) throws IOException {
|
||||
if (value == null) {
|
||||
writer.nullValue();
|
||||
return;
|
||||
}
|
||||
String formatted = formatValue(value);
|
||||
if (formatted == null) {
|
||||
writer.nullValue();
|
||||
return;
|
||||
}
|
||||
DocPropertyType docType = docType();
|
||||
if (docType == DocPropertyType.OBJECT || docType == DocPropertyType.LIST || docType == DocPropertyType.ROOT || likelyRawJson(formatted)) {
|
||||
writer.rawValue(formatted);
|
||||
} else {
|
||||
writer.value(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean likelyRawJson(String formatted) {
|
||||
String trimmed = formatted.trim();
|
||||
return !trimmed.isEmpty() && (trimmed.charAt(0) == '{' || trimmed.charAt(0) == '[');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebean.core.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
@@ -83,20 +83,31 @@ public abstract class ScalarTypeBaseDate<T> extends ScalarTypeBase<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser) throws IOException {
|
||||
if (JsonToken.VALUE_NUMBER_INT == parser.getCurrentToken()) {
|
||||
return convertFromMillis(parser.getLongValue());
|
||||
} else {
|
||||
return convertFromDate(Date.valueOf(parser.getText()));
|
||||
public T jsonRead(JsonReader parser) throws IOException {
|
||||
Token token = parser.currentToken();
|
||||
if (Token.NUMBER == token) {
|
||||
return convertFromMillis(parser.readLong());
|
||||
}
|
||||
if (Token.STRING == token) {
|
||||
return convertFromDate(Date.valueOf(parser.readString()));
|
||||
}
|
||||
|
||||
String raw = parser.readRaw();
|
||||
if (raw == null || "null".equals(raw)) {
|
||||
return null;
|
||||
}
|
||||
if (raw.length() > 1 && raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') {
|
||||
return convertFromDate(Date.valueOf(raw.substring(1, raw.length() - 1)));
|
||||
}
|
||||
return convertFromMillis(Long.parseLong(raw));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, T value) throws IOException {
|
||||
if (mode == JsonConfig.Date.ISO8601) {
|
||||
writer.writeString(toIsoFormat(value));
|
||||
writer.value(toIsoFormat(value));
|
||||
} else {
|
||||
writer.writeNumber(convertToMillis(value));
|
||||
writer.value(convertToMillis(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package io.ebean.core.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.config.JsonConfig;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -99,35 +100,53 @@ public abstract class ScalarTypeBaseDateTime<T> extends ScalarTypeBase<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser) throws IOException {
|
||||
switch (parser.getCurrentToken()) {
|
||||
case VALUE_NUMBER_INT: {
|
||||
return convertFromMillis(parser.getLongValue());
|
||||
}
|
||||
case VALUE_NUMBER_FLOAT: {
|
||||
BigDecimal value = parser.getDecimalValue();
|
||||
Timestamp timestamp = ScalarTypeUtils.toTimestamp(value);
|
||||
return convertFromTimestamp(timestamp);
|
||||
}
|
||||
default: {
|
||||
return fromJsonISO8601(parser.getText());
|
||||
}
|
||||
public T jsonRead(JsonReader parser) throws IOException {
|
||||
Token token = parser.currentToken();
|
||||
if (token == Token.NUMBER) {
|
||||
return readNumber(parser.readDecimal());
|
||||
}
|
||||
if (token == Token.STRING) {
|
||||
return fromStringValue(parser.readString());
|
||||
}
|
||||
|
||||
String raw = parser.readRaw();
|
||||
if (raw == null || "null".equals(raw)) {
|
||||
return null;
|
||||
}
|
||||
if (raw.length() > 1 && raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') {
|
||||
return fromStringValue(raw.substring(1, raw.length() - 1));
|
||||
}
|
||||
return readNumber(new BigDecimal(raw));
|
||||
}
|
||||
|
||||
private T fromStringValue(String value) {
|
||||
if (value.indexOf('-') == -1 && Character.isDigit(value.charAt(0))) {
|
||||
return readNumber(new BigDecimal(value));
|
||||
}
|
||||
return fromJsonISO8601(value);
|
||||
}
|
||||
|
||||
private T readNumber(BigDecimal value) {
|
||||
if (value.scale() <= 0) {
|
||||
return convertFromMillis(value.longValue());
|
||||
}
|
||||
Timestamp timestamp = ScalarTypeUtils.toTimestamp(value);
|
||||
return convertFromTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, T value) throws IOException {
|
||||
switch (mode) {
|
||||
case ISO8601: {
|
||||
writer.writeString(toJsonISO8601(value));
|
||||
writer.value(toJsonISO8601(value));
|
||||
break;
|
||||
}
|
||||
case NANOS: {
|
||||
writer.writeNumber(toJsonNanos(value));
|
||||
writer.value(toJsonNanos(value));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
writer.writeNumber(convertToMillis(value));
|
||||
writer.value(convertToMillis(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.core.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -104,13 +104,16 @@ public abstract class ScalarTypeBaseVarchar<T> extends ScalarTypeBase<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser) throws IOException {
|
||||
return parse(parser.getValueAsString());
|
||||
public T jsonRead(JsonReader parser) throws IOException {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
return parse(parser.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
writer.writeString(format(value));
|
||||
public void jsonWrite(JsonWriter writer, T value) throws IOException {
|
||||
writer.value(format(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,8 +4,7 @@ module io.ebean.core.type {
|
||||
|
||||
requires transitive java.sql;
|
||||
requires transitive io.ebean.api;
|
||||
requires transitive io.avaje.json;
|
||||
requires static org.postgresql.jdbc;
|
||||
|
||||
requires static com.fasterxml.jackson.core;
|
||||
|
||||
}
|
||||
|
||||
+7
-15
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-json</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -52,7 +52,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -138,14 +138,6 @@
|
||||
<!-- <optional>true</optional>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
@@ -165,21 +157,21 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>16.11.1</version>
|
||||
<version>18.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebean.text.json.JsonWriteOptions;
|
||||
@@ -17,7 +17,7 @@ public interface SpiJsonContext extends JsonContext {
|
||||
/**
|
||||
* Create a Json Writer for writing beans as JSON.
|
||||
*/
|
||||
SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options);
|
||||
SpiJsonWriter createJsonWriter(JsonWriter gen, JsonWriteOptions options);
|
||||
|
||||
/**
|
||||
* Create a Json Writer for writing beans as JSON supplying a writer.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.api.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -14,7 +14,9 @@ public interface SpiJsonReader {
|
||||
|
||||
PersistenceContext persistenceContext();
|
||||
|
||||
SpiJsonReader forJson(JsonParser moreJson);
|
||||
SpiJsonReader forJson(JsonReader moreJson);
|
||||
|
||||
SpiJsonReader forJson(String moreJson);
|
||||
|
||||
<T> void persistenceContextPut(Object beanId, T currentBean);
|
||||
|
||||
@@ -22,9 +24,9 @@ public interface SpiJsonReader {
|
||||
|
||||
ObjectMapper mapper();
|
||||
|
||||
JsonParser parser();
|
||||
JsonReader parser();
|
||||
|
||||
JsonToken nextToken() throws IOException;
|
||||
Token nextToken() throws IOException;
|
||||
|
||||
void pushPath(String path);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.changelog;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebean.event.changelog.ChangeType;
|
||||
@@ -15,13 +15,13 @@ import java.util.Map;
|
||||
*/
|
||||
final class ChangeJsonBuilder {
|
||||
|
||||
private final JsonFactory jsonFactory = new JsonFactory();
|
||||
private final JsonStream jsonStream = JsonStream.builder().build();
|
||||
|
||||
/**
|
||||
* Write the bean change as JSON.
|
||||
*/
|
||||
void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet) throws IOException {
|
||||
try (JsonGenerator generator = jsonFactory.createGenerator(writer)) {
|
||||
try (JsonWriter generator = jsonStream.writer(writer)) {
|
||||
writeBeanChange(generator, bean, changeSet);
|
||||
generator.flush();
|
||||
}
|
||||
@@ -30,58 +30,67 @@ final class ChangeJsonBuilder {
|
||||
/**
|
||||
* Write the bean change as JSON document containing the transaction header details.
|
||||
*/
|
||||
private void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet) throws IOException {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("ts", bean.getEventTime());
|
||||
gen.writeStringField("change", bean.getEvent().getCode());
|
||||
gen.writeStringField("type", bean.getType());
|
||||
gen.writeStringField("id", bean.getId().toString());
|
||||
private void writeBeanChange(JsonWriter gen, BeanChange bean, ChangeSet changeSet) {
|
||||
gen.beginObject();
|
||||
gen.name("ts");
|
||||
gen.value(bean.getEventTime());
|
||||
gen.name("change");
|
||||
gen.value(bean.getEvent().getCode());
|
||||
gen.name("type");
|
||||
gen.value(bean.getType());
|
||||
gen.name("id");
|
||||
gen.value(bean.getId().toString());
|
||||
if (bean.getTenantId() != null) {
|
||||
gen.writeStringField("tenantId", bean.getTenantId().toString());
|
||||
gen.name("tenantId");
|
||||
gen.value(bean.getTenantId().toString());
|
||||
}
|
||||
writeBeanTransactionDetails(gen, changeSet);
|
||||
writeBeanValues(gen, bean);
|
||||
gen.writeEndObject();
|
||||
gen.endObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalise by writing the transaction header details.
|
||||
*/
|
||||
private void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet) throws IOException {
|
||||
private void writeBeanTransactionDetails(JsonWriter gen, ChangeSet changeSet) {
|
||||
String source = changeSet.getSource();
|
||||
if (source != null) {
|
||||
gen.writeStringField("source", source);
|
||||
gen.name("source");
|
||||
gen.value(source);
|
||||
}
|
||||
String userId = changeSet.getUserId();
|
||||
if (userId != null) {
|
||||
gen.writeStringField("userId", userId);
|
||||
gen.name("userId");
|
||||
gen.value(userId);
|
||||
}
|
||||
String userIpAddress = changeSet.getUserIpAddress();
|
||||
if (userIpAddress != null) {
|
||||
gen.writeStringField("userIpAddress", userIpAddress);
|
||||
gen.name("userIpAddress");
|
||||
gen.value(userIpAddress);
|
||||
}
|
||||
Map<String, String> userContext = changeSet.getUserContext();
|
||||
if (userContext != null && !userContext.isEmpty()) {
|
||||
gen.writeObjectFieldStart("userContext");
|
||||
gen.name("userContext");
|
||||
gen.beginObject();
|
||||
for (Map.Entry<String, String> entry : userContext.entrySet()) {
|
||||
gen.writeStringField(entry.getKey(), entry.getValue());
|
||||
gen.name(entry.getKey());
|
||||
gen.value(entry.getValue());
|
||||
}
|
||||
gen.writeEndObject();
|
||||
gen.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For insert and update write the new/old values.
|
||||
*/
|
||||
private void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
|
||||
private void writeBeanValues(JsonWriter gen, BeanChange bean) {
|
||||
if (bean.getEvent() != ChangeType.DELETE) {
|
||||
gen.writeFieldName("data");
|
||||
gen.writeRaw(":");
|
||||
gen.writeRaw(bean.getData());
|
||||
gen.name("data");
|
||||
gen.rawValue(bean.getData());
|
||||
String oldData = bean.getOldData();
|
||||
if (oldData != null) {
|
||||
gen.writeRaw(",\"oldData\":");
|
||||
gen.writeRaw(oldData);
|
||||
gen.name("oldData");
|
||||
gen.rawValue(oldData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.DatabaseBuilder;
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.annotation.Platform;
|
||||
@@ -89,7 +89,7 @@ public final class InternalConfiguration {
|
||||
private final boolean jacksonCorePresent;
|
||||
private final ExpressionFactory expressionFactory;
|
||||
private final SpiBackgroundExecutor backgroundExecutor;
|
||||
private final JsonFactory jsonFactory;
|
||||
private final JsonStream jsonStream;
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
private final List<Plugin> plugins = new ArrayList<>();
|
||||
private final MultiValueBind multiValueBind;
|
||||
@@ -108,7 +108,7 @@ public final class InternalConfiguration {
|
||||
this.tableModState = new TableModState();
|
||||
this.logManager = initLogManager();
|
||||
this.docStoreFactory = initDocStoreFactory(service(DocStoreFactory.class));
|
||||
this.jsonFactory = config.getJsonFactory();
|
||||
this.jsonStream = config.getJsonStream();
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.bootupClasses = bootupClasses;
|
||||
@@ -292,7 +292,7 @@ public final class InternalConfiguration {
|
||||
}
|
||||
|
||||
SpiJsonContext createJsonContext(SpiEbeanServer server) {
|
||||
return jacksonCorePresent ? new DJsonContext(server, jsonFactory, typeManager) : null;
|
||||
return jacksonCorePresent ? new DJsonContext(server, jsonStream, typeManager) : null;
|
||||
}
|
||||
|
||||
AutoTuneService createAutoTuneService(SpiEbeanServer server) {
|
||||
|
||||
@@ -2,8 +2,11 @@ package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.bean.BeanDiffVisitor;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.json.WriteJson;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
@@ -13,26 +16,18 @@ import java.io.StringWriter;
|
||||
*/
|
||||
final class BeanChangeJson implements BeanDiffVisitor {
|
||||
|
||||
private final StringWriter newData;
|
||||
private final StringWriter oldData;
|
||||
private final SpiJsonWriter newJson;
|
||||
private final SpiJsonWriter oldJson;
|
||||
private final StringWriter data;
|
||||
private final SpiJsonWriter json;
|
||||
private final boolean writeNew;
|
||||
private final ArrayStack<BeanDescriptor<?>> stack = new ArrayStack<>();
|
||||
private BeanDescriptor<?> descriptor;
|
||||
|
||||
BeanChangeJson(BeanDescriptor<?> descriptor, boolean statelessUpdate) {
|
||||
BeanChangeJson(BeanDescriptor<?> descriptor, boolean writeNew) {
|
||||
this.descriptor = descriptor;
|
||||
this.newData = new StringWriter(200);
|
||||
this.newJson = descriptor.createJsonWriter(newData);
|
||||
newJson.writeStartObject();
|
||||
if (statelessUpdate) {
|
||||
this.oldJson = null;
|
||||
this.oldData = null;
|
||||
} else {
|
||||
this.oldData = new StringWriter(200);
|
||||
this.oldJson = descriptor.createJsonWriter(oldData);
|
||||
oldJson.writeStartObject();
|
||||
}
|
||||
this.writeNew = writeNew;
|
||||
this.data = new StringWriter(200);
|
||||
this.json = new WriteJson(JsonStream.builder().build().writer(data), JsonConfig.Include.ALL);
|
||||
json.writeStartObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,10 +35,7 @@ final class BeanChangeJson implements BeanDiffVisitor {
|
||||
try {
|
||||
BeanProperty prop = descriptor.propertiesIndex[position];
|
||||
if (prop.isDbUpdatable()) {
|
||||
prop.jsonWriteValue(newJson, newVal);
|
||||
if (oldJson != null) {
|
||||
prop.jsonWriteValue(oldJson, oldVal);
|
||||
}
|
||||
prop.jsonWriteValue(json, writeNew ? newVal : oldVal);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
@@ -55,18 +47,12 @@ final class BeanChangeJson implements BeanDiffVisitor {
|
||||
stack.push(descriptor);
|
||||
BeanPropertyAssocOne<?> embedded = (BeanPropertyAssocOne<?>)descriptor.propertiesIndex[position];
|
||||
descriptor = embedded.targetDescriptor();
|
||||
newJson.writeStartObject(embedded.name());
|
||||
if (oldJson != null) {
|
||||
oldJson.writeStartObject(embedded.name());
|
||||
}
|
||||
json.writeStartObject(embedded.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPop() {
|
||||
newJson.writeEndObject();
|
||||
if (oldJson != null) {
|
||||
oldJson.writeEndObject();
|
||||
}
|
||||
json.writeEndObject();
|
||||
descriptor = stack.pop();
|
||||
}
|
||||
|
||||
@@ -75,28 +61,14 @@ final class BeanChangeJson implements BeanDiffVisitor {
|
||||
*/
|
||||
void flush() {
|
||||
try {
|
||||
newJson.writeEndObject();
|
||||
newJson.flush();
|
||||
if (oldJson != null) {
|
||||
oldJson.writeEndObject();
|
||||
oldJson.flush();
|
||||
}
|
||||
json.writeEndObject();
|
||||
json.flush();
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new values JSON.
|
||||
*/
|
||||
String newJson() {
|
||||
return newData.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the old values JSON.
|
||||
*/
|
||||
String oldJson() {
|
||||
return oldData == null ? null : oldData.toString();
|
||||
String json() {
|
||||
return data.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -792,10 +792,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
*/
|
||||
private BeanChange updateBeanChange(PersistRequestBean<T> request) {
|
||||
try {
|
||||
BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate());
|
||||
request.intercept().addDirtyPropertyValues(changeJson);
|
||||
changeJson.flush();
|
||||
return beanChange(ChangeType.UPDATE, request.beanId(), changeJson.newJson(), changeJson.oldJson());
|
||||
BeanChangeJson newValues = new BeanChangeJson(this, true);
|
||||
request.intercept().addDirtyPropertyValues(newValues);
|
||||
newValues.flush();
|
||||
String oldData = null;
|
||||
if (!request.isStatelessUpdate()) {
|
||||
BeanChangeJson oldValues = new BeanChangeJson(this, false);
|
||||
request.intercept().addDirtyPropertyValues(oldValues);
|
||||
oldValues.flush();
|
||||
oldData = oldValues.json();
|
||||
}
|
||||
return beanChange(ChangeType.UPDATE, request.beanId(), newValues.json(), oldData);
|
||||
} catch (RuntimeException e) {
|
||||
log.log(ERROR, "Failed to write ChangeLog entry for update", e);
|
||||
return null;
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
@@ -49,25 +49,25 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
JsonParser parser = readJson.parser();
|
||||
JsonReader parser = readJson.parser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
String fieldName = parser.nextFieldName();
|
||||
if (fieldName == null) {
|
||||
break;
|
||||
}
|
||||
parser.beginObject();
|
||||
while (parser.hasNextField()) {
|
||||
String fieldName = parser.nextField();
|
||||
if (stringKey) {
|
||||
parser.nextToken();
|
||||
Object val = readJsonElement(readJson, null, null); // CHECKME: Update existing map entry here?
|
||||
add.addKeyValue(fieldName, val);
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
parser.beginObject();
|
||||
parser.nextField();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
parser.nextFieldName();
|
||||
parser.nextField();
|
||||
Object val = readJsonElement(readJson, null, null); // CHECKME: Update existing map entry here?
|
||||
parser.endObject();
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
parser.endObject();
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
|
||||
+6
-9
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
@@ -40,15 +39,13 @@ class BeanDescriptorElementScalar<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
JsonParser parser = readJson.parser();
|
||||
JsonReader parser = readJson.parser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
|
||||
break;
|
||||
}
|
||||
parser.beginArray();
|
||||
while (parser.hasNextElement()) {
|
||||
add.addElement(scalarType.jsonRead(parser));
|
||||
} while (true);
|
||||
}
|
||||
parser.endArray();
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
@@ -49,25 +49,25 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
JsonParser parser = readJson.parser();
|
||||
JsonReader parser = readJson.parser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
String fieldName = parser.nextFieldName();
|
||||
if (fieldName == null) {
|
||||
break;
|
||||
}
|
||||
parser.beginObject();
|
||||
while (parser.hasNextField()) {
|
||||
String fieldName = parser.nextField();
|
||||
if (stringKey) {
|
||||
parser.nextToken();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(fieldName, val);
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
parser.beginObject();
|
||||
parser.nextField();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
parser.nextFieldName();
|
||||
parser.nextField();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
parser.endObject();
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
parser.endObject();
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
|
||||
+37
-50
@@ -1,10 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
@@ -34,7 +31,8 @@ final class BeanDescriptorJsonHelp<T> {
|
||||
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
|
||||
String discValue = localInheritInfo.getDiscriminatorStringValue();
|
||||
String discColumn = localInheritInfo.getDiscriminatorColumn();
|
||||
writeJson.gen().writeStringField(discColumn, discValue);
|
||||
writeJson.gen().name(discColumn);
|
||||
writeJson.gen().value(discValue);
|
||||
localInheritInfo.desc().jsonWriteProperties(writeJson, bean);
|
||||
}
|
||||
writeJson.writeEndObject();
|
||||
@@ -66,44 +64,38 @@ final class BeanDescriptorJsonHelp<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T jsonRead(SpiJsonReader jsonRead, String path, boolean withInheritance, T target) throws IOException {
|
||||
JsonParser parser = jsonRead.parser();
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (parser.getCurrentToken() == JsonToken.START_OBJECT) {
|
||||
// start object token read by Jackson already
|
||||
} else {
|
||||
// check for null or start object
|
||||
JsonToken token = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
|
||||
return null;
|
||||
}
|
||||
if (JsonToken.START_OBJECT != token) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + token + " - expecting start_object", parser.getCurrentLocation());
|
||||
}
|
||||
JsonReader parser = jsonRead.parser();
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
Token token = parser.currentToken();
|
||||
if (token != Token.BEGIN_OBJECT) {
|
||||
throw new IllegalStateException("Unexpected token " + token + " - expecting BEGIN_OBJECT at: " + parser.location());
|
||||
}
|
||||
|
||||
if (desc.inheritInfo == null || !withInheritance) {
|
||||
return jsonReadObject(jsonRead, path, target);
|
||||
}
|
||||
|
||||
ObjectNode node = jsonRead.mapper().readTree(parser);
|
||||
if (node.isNull()) {
|
||||
Map<String, Object> node = EJson.parseObject(parser);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
JsonParser newParser = node.traverse();
|
||||
SpiJsonReader newReader = jsonRead.forJson(newParser);
|
||||
|
||||
// check for the discriminator value to determine the correct sub type
|
||||
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
|
||||
JsonNode discNode = node.get(discColumn);
|
||||
if (discNode == null || discNode.isNull()) {
|
||||
Object discValue = node.get(discColumn);
|
||||
String rawObject = EJson.write(node);
|
||||
SpiJsonReader newReader = jsonRead.forJson(rawObject);
|
||||
if (discValue == null) {
|
||||
if (!desc.isAbstractType()) {
|
||||
return desc.jsonReadObject(newReader, path, target);
|
||||
}
|
||||
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
|
||||
throw new JsonParseException(newParser, msg, parser.getCurrentLocation());
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
BeanDescriptor<T> inheritDesc = (BeanDescriptor<T>) inheritInfo.readType(discNode.asText()).desc();
|
||||
BeanDescriptor<T> inheritDesc = (BeanDescriptor<T>) inheritInfo.readType(String.valueOf(discValue)).desc();
|
||||
return inheritDesc.jsonReadObject(newReader, path, target);
|
||||
}
|
||||
|
||||
@@ -124,35 +116,30 @@ final class BeanDescriptorJsonHelp<T> {
|
||||
if (path != null) {
|
||||
readJson.pushPath(path);
|
||||
}
|
||||
JsonReader parser = readJson.parser();
|
||||
parser.beginObject();
|
||||
|
||||
// unmapped properties, send to JsonReadBeanVisitor later
|
||||
Map<String, Object> unmappedProperties = null;
|
||||
do {
|
||||
JsonParser parser = readJson.parser();
|
||||
JsonToken event = parser.nextToken();
|
||||
if (JsonToken.FIELD_NAME == event) {
|
||||
String key = parser.getCurrentName();
|
||||
BeanProperty p = desc.beanProperty(key);
|
||||
if (p != null) {
|
||||
if (p.isVersion() && readJson.update() ) {
|
||||
// skip version prop during update
|
||||
p.jsonRead(readJson);
|
||||
} else {
|
||||
p.jsonRead(readJson, bean);
|
||||
}
|
||||
while (parser.hasNextField()) {
|
||||
String key = parser.nextField();
|
||||
BeanProperty p = desc.beanProperty(key);
|
||||
if (p != null) {
|
||||
if (p.isVersion() && readJson.update()) {
|
||||
// skip version prop during update
|
||||
p.jsonRead(readJson);
|
||||
} else {
|
||||
// read an unmapped property
|
||||
if (unmappedProperties == null) {
|
||||
unmappedProperties = new LinkedHashMap<>();
|
||||
}
|
||||
unmappedProperties.put(key, EJson.parse(parser));
|
||||
p.jsonRead(readJson, bean);
|
||||
}
|
||||
} else if (JsonToken.END_OBJECT == event) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected token " + event + " - expecting key or end_object at: " + parser.getCurrentLocation());
|
||||
// read an unmapped property
|
||||
if (unmappedProperties == null) {
|
||||
unmappedProperties = new LinkedHashMap<>();
|
||||
}
|
||||
unmappedProperties.put(key, EJson.parse(parser));
|
||||
}
|
||||
|
||||
} while (true);
|
||||
}
|
||||
parser.endObject();
|
||||
|
||||
if (unmappedProperties != null) {
|
||||
desc.setUnmappedJson(bean, unmappedProperties);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.ebean.DataIntegrityException;
|
||||
import io.ebean.ModifyAwareType;
|
||||
import io.ebean.ValuePair;
|
||||
@@ -1437,14 +1438,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
|
||||
public Object jsonRead(SpiJsonReader ctx) throws IOException {
|
||||
JsonToken event = ctx.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
JsonReader parser = ctx.parser();
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
} else {
|
||||
// expect to read non-null json value
|
||||
Object objValue;
|
||||
if (scalarType != null) {
|
||||
objValue = scalarType.jsonRead(ctx.parser());
|
||||
objValue = scalarType.jsonRead(parser);
|
||||
} else {
|
||||
try {
|
||||
objValue = ctx.readValueUsingObjectMapper(propertyType);
|
||||
|
||||
+87
-30
@@ -1,15 +1,14 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.plugin.PropertyAssocMany;
|
||||
import io.ebean.text.PathProperties;
|
||||
@@ -1043,9 +1042,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
*/
|
||||
private Object jsonReadCollection(String json) throws IOException {
|
||||
SpiJsonReader ctx = descriptor.createJsonReader(json);
|
||||
JsonParser parser = ctx.parser();
|
||||
JsonToken event = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
JsonReader parser = ctx.parser();
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
return jsonReadCollection(ctx, null, null);
|
||||
@@ -1068,20 +1066,18 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
BeanCollection<?> collection = createEmpty(parentBean);
|
||||
BeanCollectionAdd add = beanCollectionAdd(collection);
|
||||
Map<Object, T> existingBeans = extractBeans(targets);
|
||||
do {
|
||||
|
||||
JsonReader parser = readJson.parser();
|
||||
parser.beginArray();
|
||||
while (parser.hasNextElement()) {
|
||||
EntityBean detailBean = getDetailBean(readJson, existingBeans);
|
||||
if (detailBean == null) {
|
||||
// read the entire array
|
||||
break;
|
||||
if (detailBean != null) {
|
||||
add.addEntityBean(detailBean);
|
||||
if (parentBean != null && childMasterProperty != null) {
|
||||
// bind detail bean back to master via mappedBy property
|
||||
childMasterProperty.setValue(detailBean, parentBean);
|
||||
}
|
||||
}
|
||||
add.addEntityBean(detailBean);
|
||||
|
||||
if (parentBean != null && childMasterProperty != null) {
|
||||
// bind detail bean back to master via mappedBy property
|
||||
childMasterProperty.setValue(detailBean, parentBean);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
@@ -1092,18 +1088,79 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
BeanProperty idProperty = targetDescriptor.idProperty();
|
||||
if (targets == null || idProperty == null) {
|
||||
return (EntityBean) targetDescriptor.jsonRead(readJson, name, null);
|
||||
} else {
|
||||
JsonToken token = readJson.parser().nextToken();
|
||||
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
|
||||
return null;
|
||||
}
|
||||
// extract the id. We have to buffer the JSON;
|
||||
ObjectNode node = readJson.mapper().readTree(readJson.parser());
|
||||
SpiJsonReader jsonReader = readJson.forJson(node.traverse());
|
||||
JsonNode idNode = node.get(idProperty.name());
|
||||
Object id = idNode == null ? null : idProperty.jsonRead(readJson.forJson(idNode.traverse()));
|
||||
return (EntityBean) targetDescriptor.jsonRead(jsonReader, name, targets.get(id));
|
||||
}
|
||||
/// ROB Checkme
|
||||
JsonReader parser = readJson.parser();
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
EntityBean incomingBean = (EntityBean) targetDescriptor.jsonRead(readJson, name, null);
|
||||
Object id = idProperty.getValue(incomingBean);
|
||||
EntityBean existingBean = (EntityBean) targets.get(id);
|
||||
if (existingBean == null) {
|
||||
return incomingBean;
|
||||
}
|
||||
mergeLoadedBean(targetDescriptor, incomingBean, existingBean);
|
||||
return existingBean;
|
||||
}
|
||||
|
||||
private void mergeLoadedBean(BeanDescriptor<?> descriptor, EntityBean incomingBean, EntityBean existingBean) {
|
||||
EntityBeanIntercept incomingIntercept = incomingBean._ebean_getIntercept();
|
||||
for (BeanProperty property : descriptor.propertiesBaseScalar()) {
|
||||
if (incomingIntercept.isLoadedProperty(property.propertyIndex())) {
|
||||
property.setValueIntercept(existingBean, property.getValue(incomingBean));
|
||||
}
|
||||
}
|
||||
for (BeanPropertyAssocMany<?> property : descriptor.propertiesMany()) {
|
||||
if (incomingIntercept.isLoadedProperty(property.propertyIndex())) {
|
||||
Object mergedValue = mergeLoadedMany(property, property.getValue(incomingBean), property.getValue(existingBean));
|
||||
property.setValueIntercept(existingBean, mergedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object mergeLoadedMany(BeanPropertyAssocMany<?> property, Object incomingValue, Object existingValue) {
|
||||
if (!(incomingValue instanceof Collection<?>)) {
|
||||
return incomingValue;
|
||||
}
|
||||
if (!(existingValue instanceof Collection<?>)) {
|
||||
return incomingValue;
|
||||
}
|
||||
Collection<?> incomingCollection = (Collection<?>) incomingValue;
|
||||
Collection<?> existingCollection = (Collection<?>) existingValue;
|
||||
BeanProperty idProperty = property.targetDescriptor.idProperty();
|
||||
if (idProperty == null) {
|
||||
return incomingValue;
|
||||
}
|
||||
|
||||
Map<Object, EntityBean> existingById = new LinkedHashMap<>();
|
||||
for (Object existingElement : existingCollection) {
|
||||
if (existingElement instanceof EntityBean) {
|
||||
EntityBean existingBean = (EntityBean) existingElement;
|
||||
Object id = idProperty.getValue(existingBean);
|
||||
if (id != null) {
|
||||
existingById.put(id, existingBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BeanCollection<?> mergedCollection = property.createEmpty(null);
|
||||
BeanCollectionAdd add = property.beanCollectionAdd(mergedCollection);
|
||||
for (Object incomingElement : incomingCollection) {
|
||||
if (!(incomingElement instanceof EntityBean)) {
|
||||
continue;
|
||||
}
|
||||
EntityBean incomingBean = (EntityBean) incomingElement;
|
||||
Object id = idProperty.getValue(incomingBean);
|
||||
EntityBean existingBean = id == null ? null : existingById.get(id);
|
||||
if (existingBean == null) {
|
||||
add.addEntityBean(incomingBean);
|
||||
} else {
|
||||
mergeLoadedBean(property.targetDescriptor, incomingBean, existingBean);
|
||||
add.addEntityBean(existingBean);
|
||||
}
|
||||
}
|
||||
return mergedCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-8
@@ -1,8 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
|
||||
@@ -38,17 +37,17 @@ class BeanPropertyAssocManyJsonHelp {
|
||||
if (!this.many.jsonDeserialize) {
|
||||
return;
|
||||
}
|
||||
JsonParser parser = readJson.parser();
|
||||
JsonToken event = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
JsonReader parser = readJson.parser();
|
||||
if (parser.isNullValue()) {
|
||||
return;
|
||||
}
|
||||
if (many.isTransient()) {
|
||||
jsonReadTransientUsingObjectMapper(readJson, parentBean);
|
||||
return;
|
||||
}
|
||||
if (JsonToken.START_ARRAY != event && JsonToken.START_OBJECT != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start array or object");
|
||||
Token event = parser.currentToken();
|
||||
if (Token.BEGIN_ARRAY != event && Token.BEGIN_OBJECT != event) {
|
||||
throw new IllegalStateException("Unexpected token " + event + " - expecting start array or object");
|
||||
}
|
||||
if (readJson.update()) {
|
||||
many.setValueIntercept(parentBean, many.jsonReadCollection(readJson, parentBean, many.getValue(parentBean)));
|
||||
|
||||
+2
-2
@@ -29,11 +29,11 @@ class BeanPropertyAssocManyJsonTransient {
|
||||
TypeFactory typeFactory = mapper.getTypeFactory();
|
||||
JavaType target = typeFactory.constructType(many.targetType());
|
||||
MapType jacksonType = typeFactory.constructMapType(LinkedHashMap.class, TypeFactory.unknownType(), target);
|
||||
value = mapper.readValue(readJson.parser(), jacksonType);
|
||||
value = mapper.readValue(readJson.parser().readRaw(), jacksonType);
|
||||
} else {
|
||||
// read list or set using Jackson object mapper
|
||||
CollectionType jacksonType = mapper.getTypeFactory().constructCollectionType(manyType.getCollectionType(), many.targetType());
|
||||
value = mapper.readValue(readJson.parser(), jacksonType);
|
||||
value = mapper.readValue(readJson.parser().readRaw(), jacksonType);
|
||||
}
|
||||
many.setValue(parentBean, value);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.text.json.JsonBeanReader;
|
||||
@@ -45,7 +45,7 @@ public final class DJsonBeanReader<T> implements JsonBeanReader<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonBeanReader<T> forJson(JsonParser moreJson) {
|
||||
public JsonBeanReader<T> forJson(JsonReader moreJson) {
|
||||
return new DJsonBeanReader<>(desc, readJson.forJson(moreJson));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package io.ebeaninternal.server.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.core.PrettyPrinter;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.config.JsonConfig;
|
||||
@@ -28,7 +25,6 @@ import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -45,30 +41,22 @@ import java.util.Set;
|
||||
*/
|
||||
public final class DJsonContext implements SpiJsonContext {
|
||||
|
||||
private static final PrettyPrinter PRETTY_PRINTER = new Pretty();
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
private final JsonFactory jsonFactory;
|
||||
private final JsonStream jsonStream;
|
||||
private final Object defaultObjectMapper;
|
||||
private final JsonConfig.Include defaultInclude;
|
||||
private final DJsonScalar jsonScalar;
|
||||
|
||||
private static class Pretty extends DefaultPrettyPrinter {
|
||||
Pretty() {
|
||||
_objectFieldValueSeparatorWithSpaces = ": ";
|
||||
}
|
||||
}
|
||||
|
||||
public DJsonContext(SpiEbeanServer server, JsonFactory jsonFactory, TypeManager typeManager) {
|
||||
public DJsonContext(SpiEbeanServer server, JsonStream jsonStream, TypeManager typeManager) {
|
||||
this.server = server;
|
||||
this.jsonFactory = (jsonFactory != null) ? jsonFactory : new JsonFactory();
|
||||
this.jsonStream = jsonStream;
|
||||
this.defaultObjectMapper = this.server.config().getObjectMapper();
|
||||
this.defaultInclude = this.server.config().getJsonInclude();
|
||||
this.jsonScalar = new DJsonScalar(typeManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeScalar(JsonGenerator generator, Object scalarValue) throws IOException {
|
||||
public void writeScalar(JsonWriter generator, Object scalarValue) throws IOException {
|
||||
jsonScalar.write(generator, scalarValue);
|
||||
}
|
||||
|
||||
@@ -78,31 +66,34 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonGenerator createGenerator(Writer writer) throws JsonIOException {
|
||||
public JsonWriter createGenerator(Writer writer) throws JsonIOException {
|
||||
JsonWriter jsonWriter = stream().writer(writer);
|
||||
jsonWriter.serializeNulls(defaultInclude == JsonConfig.Include.ALL);
|
||||
jsonWriter.serializeEmpty(defaultInclude != JsonConfig.Include.NON_EMPTY);
|
||||
return jsonWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonReader createParser(Reader reader) throws JsonIOException {
|
||||
try {
|
||||
return jsonFactory.createGenerator(writer);
|
||||
return createParser(readAll(reader));
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonParser createParser(Reader reader) throws JsonIOException {
|
||||
try {
|
||||
return jsonFactory.createParser(reader);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
private JsonStream stream() {
|
||||
return (jsonStream != null) ? jsonStream : JsonStream.builder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toBean(Class<T> cls, String json) throws JsonIOException {
|
||||
return toBean(cls, new StringReader(json));
|
||||
return toBean(cls, createParser(json));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toBean(Class<T> cls, String json, JsonReadOptions options) throws JsonIOException {
|
||||
return toBean(cls, new StringReader(json), options);
|
||||
return toBean(cls, createParser(json), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,15 +107,15 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toBean(Class<T> cls, JsonParser parser) throws JsonIOException {
|
||||
public <T> T toBean(Class<T> cls, JsonReader parser) throws JsonIOException {
|
||||
return toBean(cls, parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
public <T> T toBean(Class<T> cls, JsonReader parser, JsonReadOptions options) throws JsonIOException {
|
||||
BeanDescriptor<T> desc = getDescriptor(cls);
|
||||
try {
|
||||
return desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), false), null, null);
|
||||
return desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), false, stream()), null, null);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
@@ -132,12 +123,12 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
|
||||
@Override
|
||||
public <T> void toBean(T target, String json) throws JsonIOException {
|
||||
toBean(target, new StringReader(json));
|
||||
toBean(target, createParser(json));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void toBean(T target, String json, JsonReadOptions options) throws JsonIOException {
|
||||
toBean(target, new StringReader(json), options);
|
||||
toBean(target, createParser(json), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,42 +142,42 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void toBean(T target, JsonParser parser) throws JsonIOException {
|
||||
public <T> void toBean(T target, JsonReader parser) throws JsonIOException {
|
||||
toBean(target, parser, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
public <T> void toBean(T target, JsonReader parser, JsonReadOptions options) throws JsonIOException {
|
||||
BeanDescriptor<T> desc = (BeanDescriptor<T>) getDescriptor(target.getClass());
|
||||
try {
|
||||
desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), target != null), null, target);
|
||||
desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), target != null, stream()), null, target);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DJsonBeanReader<T> createBeanReader(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
public <T> DJsonBeanReader<T> createBeanReader(Class<T> cls, JsonReader parser, JsonReadOptions options) throws JsonIOException {
|
||||
BeanDescriptor<T> desc = getDescriptor(cls);
|
||||
return new DJsonBeanReader<>(desc, new ReadJson(desc, parser, options, determineObjectMapper(options), false));
|
||||
return new DJsonBeanReader<>(desc, new ReadJson(desc, parser, options, determineObjectMapper(options), false, stream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DJsonBeanReader<T> createBeanReader(BeanType<T> beanType, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
public <T> DJsonBeanReader<T> createBeanReader(BeanType<T> beanType, JsonReader parser, JsonReadOptions options) throws JsonIOException {
|
||||
BeanDescriptor<T> desc = (BeanDescriptor<T>) beanType;
|
||||
SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options), false);
|
||||
SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options), false, stream());
|
||||
return new DJsonBeanReader<>(desc, readJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> toList(Class<T> cls, String json) throws JsonIOException {
|
||||
return toList(cls, new StringReader(json));
|
||||
return toList(cls, createParser(json));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> toList(Class<T> cls, String json, JsonReadOptions options) throws JsonIOException {
|
||||
return toList(cls, new StringReader(json), options);
|
||||
return toList(cls, createParser(json), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -200,35 +191,30 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> toList(Class<T> cls, JsonParser src) throws JsonIOException {
|
||||
public <T> List<T> toList(Class<T> cls, JsonReader src) throws JsonIOException {
|
||||
return toList(cls, src, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> toList(Class<T> cls, JsonParser src, JsonReadOptions options) throws JsonIOException {
|
||||
public <T> List<T> toList(Class<T> cls, JsonReader src, JsonReadOptions options) throws JsonIOException {
|
||||
BeanDescriptor<T> desc = getDescriptor(cls);
|
||||
SpiJsonReader readJson = new ReadJson(desc, src, options, determineObjectMapper(options), false);
|
||||
SpiJsonReader readJson = new ReadJson(desc, src, options, determineObjectMapper(options), false, stream());
|
||||
try {
|
||||
|
||||
JsonToken currentToken = src.getCurrentToken();
|
||||
if (currentToken != JsonToken.START_ARRAY) {
|
||||
JsonToken event = src.nextToken();
|
||||
if (event != JsonToken.START_ARRAY) {
|
||||
throw new JsonParseException(src, "Expecting start_array event but got " + event);
|
||||
}
|
||||
if (src.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
Token currentToken = src.currentToken();
|
||||
if (currentToken != Token.BEGIN_ARRAY) {
|
||||
throw new JsonIOException("Expecting BEGIN_ARRAY but got " + currentToken);
|
||||
}
|
||||
|
||||
List<T> list = new ArrayList<>();
|
||||
do {
|
||||
// CHECKME: Should we update the list
|
||||
src.beginArray();
|
||||
while (src.hasNextElement()) {
|
||||
T bean = desc.jsonRead(readJson, null, null);
|
||||
if (bean == null) {
|
||||
break;
|
||||
} else {
|
||||
if (bean != null) {
|
||||
list.add(bean);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
}
|
||||
return list;
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
@@ -237,7 +223,7 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
|
||||
@Override
|
||||
public Object toObject(Type genericType, String json) throws JsonIOException {
|
||||
return toObject(genericType, createParser(new StringReader(json)));
|
||||
return toObject(genericType, createParser(json));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -245,8 +231,22 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
return toObject(genericType, createParser(json));
|
||||
}
|
||||
|
||||
private JsonReader createParser(String json) {
|
||||
return stream().reader(json);
|
||||
}
|
||||
|
||||
private String readAll(Reader reader) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
char[] buffer = new char[2048];
|
||||
int len;
|
||||
while ((len = reader.read(buffer)) != -1) {
|
||||
builder.append(buffer, 0, len);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toObject(Type genericType, JsonParser jsonParser) throws JsonIOException {
|
||||
public Object toObject(Type genericType, JsonReader jsonParser) throws JsonIOException {
|
||||
TypeInfo info = ParamTypeHelper.getTypeInfo(genericType);
|
||||
ManyType manyType = info.getManyType();
|
||||
switch (manyType) {
|
||||
@@ -262,19 +262,19 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(Object value, JsonGenerator generator) throws JsonIOException {
|
||||
public void toJson(Object value, JsonWriter generator) throws JsonIOException {
|
||||
// generator passed in so don't close it
|
||||
toJsonNoClose(value, generator, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(Object value, JsonGenerator generator, FetchPath fetchPath) throws JsonIOException {
|
||||
public void toJson(Object value, JsonWriter generator, FetchPath fetchPath) throws JsonIOException {
|
||||
// generator passed in so don't close it
|
||||
toJsonNoClose(value, generator, JsonWriteOptions.pathProperties(fetchPath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException {
|
||||
public void toJson(Object o, JsonWriter generator, JsonWriteOptions options) throws JsonIOException {
|
||||
// generator passed in so don't close it
|
||||
toJsonNoClose(o, generator, options);
|
||||
}
|
||||
@@ -303,9 +303,9 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the JsonGenerator and close when complete.
|
||||
* Write to the JsonWriter and close when complete.
|
||||
*/
|
||||
private void toJsonWithClose(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException {
|
||||
private void toJsonWithClose(Object o, JsonWriter generator, JsonWriteOptions options) throws JsonIOException {
|
||||
try {
|
||||
toJsonInternal(o, generator, options);
|
||||
generator.close();
|
||||
@@ -315,9 +315,9 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the JsonGenerator and without closing it (as it was created externally).
|
||||
* Write to the JsonWriter and without closing it (as it was created externally).
|
||||
*/
|
||||
private void toJsonNoClose(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException {
|
||||
private void toJsonNoClose(Object o, JsonWriter generator, JsonWriteOptions options) throws JsonIOException {
|
||||
try {
|
||||
toJsonInternal(o, generator, options);
|
||||
} catch (IOException e) {
|
||||
@@ -342,9 +342,9 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
|
||||
private String toJsonString(Object value, JsonWriteOptions options, boolean pretty) throws JsonIOException {
|
||||
StringWriter writer = new StringWriter(500);
|
||||
try (JsonGenerator gen = createGenerator(writer)) {
|
||||
try (JsonWriter gen = createGenerator(writer)) {
|
||||
if (pretty) {
|
||||
gen.setPrettyPrinter(PRETTY_PRINTER);
|
||||
gen.pretty(true);
|
||||
}
|
||||
toJsonInternal(value, gen, options);
|
||||
} catch (IOException e) {
|
||||
@@ -354,29 +354,23 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void toJsonInternal(Object value, JsonGenerator gen, JsonWriteOptions options) throws IOException {
|
||||
private void toJsonInternal(Object value, JsonWriter gen, JsonWriteOptions options) throws IOException {
|
||||
if (value == null) {
|
||||
gen.writeNull();
|
||||
gen.nullValue();
|
||||
} else if (value instanceof Number) {
|
||||
gen.writeNumber(((Number) value).doubleValue());
|
||||
gen.jsonValue(value);
|
||||
} else if (value instanceof Boolean) {
|
||||
gen.writeBoolean((Boolean) value);
|
||||
gen.value((Boolean) value);
|
||||
} else if (value instanceof String) {
|
||||
gen.writeString((String) value);
|
||||
|
||||
// } else if (o instanceof JsonElement) {
|
||||
|
||||
gen.value((String) value);
|
||||
} else if (value instanceof Map<?, ?>) {
|
||||
toJsonFromMap((Map<Object, Object>) value, gen, options);
|
||||
|
||||
} else if (value instanceof Collection<?>) {
|
||||
toJsonFromCollection((Collection<?>) value, null, gen, options);
|
||||
|
||||
} else if (value instanceof EntityBean) {
|
||||
BeanDescriptor<?> d = getDescriptor(value.getClass());
|
||||
WriteJson writeJson = createWriteJson(gen, options);
|
||||
d.jsonWrite(writeJson, (EntityBean) value, null);
|
||||
|
||||
} else {
|
||||
jsonScalar.write(gen, value);
|
||||
}
|
||||
@@ -385,8 +379,8 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
@Override
|
||||
public SpiJsonReader createJsonRead(BeanType<?> beanType, String json) {
|
||||
BeanDescriptor<?> desc = (BeanDescriptor<?>) beanType;
|
||||
JsonParser parser = createParser(new StringReader(json));
|
||||
return new ReadJson(desc, parser, null, defaultObjectMapper, false);
|
||||
JsonReader parser = createParser(json);
|
||||
return new ReadJson(desc, parser, null, defaultObjectMapper, false, stream());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -395,11 +389,11 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options) {
|
||||
public SpiJsonWriter createJsonWriter(JsonWriter gen, JsonWriteOptions options) {
|
||||
return createWriteJson(gen, options);
|
||||
}
|
||||
|
||||
private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) {
|
||||
private WriteJson createWriteJson(JsonWriter gen, JsonWriteOptions options) {
|
||||
FetchPath pathProps = (options == null) ? null : options.getPathProperties();
|
||||
Map<String, JsonWriteBeanVisitor<?>> visitors = (options == null) ? null : options.getVisitorMap();
|
||||
return new WriteJson(server,
|
||||
@@ -411,46 +405,50 @@ public final class DJsonContext implements SpiJsonContext {
|
||||
options == null || options.isIncludeLoadedImplicit());
|
||||
}
|
||||
|
||||
private <T> void toJsonFromCollection(Collection<T> collection, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException {
|
||||
private <T> void toJsonFromCollection(Collection<T> collection, String key, JsonWriter gen, JsonWriteOptions options) throws IOException {
|
||||
if (key != null) {
|
||||
gen.writeFieldName(key);
|
||||
gen.name(key);
|
||||
}
|
||||
gen.writeStartArray();
|
||||
gen.beginArray();
|
||||
WriteJson writeJson = createWriteJson(gen, options);
|
||||
for (T bean : collection) {
|
||||
BeanDescriptor<?> d = getDescriptor(bean.getClass());
|
||||
d.jsonWrite(writeJson, (EntityBean) bean, null);
|
||||
if (bean == null) {
|
||||
gen.nullValue();
|
||||
} else if (bean instanceof EntityBean) {
|
||||
BeanDescriptor<?> d = getDescriptor(bean.getClass());
|
||||
d.jsonWrite(writeJson, (EntityBean) bean, null);
|
||||
} else {
|
||||
EJson.write(bean, gen);
|
||||
}
|
||||
}
|
||||
gen.writeEndArray();
|
||||
gen.endArray();
|
||||
}
|
||||
|
||||
private void toJsonFromMap(Map<Object, Object> map, JsonGenerator gen, JsonWriteOptions options) throws IOException {
|
||||
private void toJsonFromMap(Map<Object, Object> map, JsonWriter gen, JsonWriteOptions options) throws IOException {
|
||||
Set<Entry<Object, Object>> entrySet = map.entrySet();
|
||||
Iterator<Entry<Object, Object>> it = entrySet.iterator();
|
||||
|
||||
WriteJson writeJson = createWriteJson(gen, options);
|
||||
gen.writeStartObject();
|
||||
gen.beginObject();
|
||||
|
||||
while (it.hasNext()) {
|
||||
Entry<Object, Object> entry = it.next();
|
||||
String key = entry.getKey().toString();
|
||||
Object value = entry.getValue();
|
||||
if (value == null) {
|
||||
gen.writeNullField(key);
|
||||
gen.name(key);
|
||||
gen.nullValue();
|
||||
} else if (value instanceof Collection<?>) {
|
||||
toJsonFromCollection((Collection<?>) value, key, gen, options);
|
||||
} else if (value instanceof EntityBean) {
|
||||
BeanDescriptor<?> d = getDescriptor(value.getClass());
|
||||
d.jsonWrite(writeJson, (EntityBean) value, key);
|
||||
} else {
|
||||
if (value instanceof Collection<?>) {
|
||||
toJsonFromCollection((Collection<?>) value, key, gen, options);
|
||||
|
||||
} else if (value instanceof EntityBean) {
|
||||
BeanDescriptor<?> d = getDescriptor(value.getClass());
|
||||
d.jsonWrite(writeJson, (EntityBean) value, key);
|
||||
|
||||
} else {
|
||||
EJson.write(entry, gen);
|
||||
}
|
||||
gen.name(key);
|
||||
EJson.write(value, gen);
|
||||
}
|
||||
}
|
||||
gen.writeEndObject();
|
||||
gen.endObject();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
@@ -19,21 +19,18 @@ public final class DJsonScalar {
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void write(JsonGenerator gen, Object value) throws IOException {
|
||||
public void write(JsonWriter gen, Object value) throws IOException {
|
||||
if (value instanceof String) {
|
||||
gen.writeString((String) value);
|
||||
gen.value((String) value);
|
||||
|
||||
} else if (value instanceof List) {
|
||||
// expected for @DbArray values
|
||||
List list = (List)value;
|
||||
gen.writeRaw('[');
|
||||
gen.beginArray();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i > 0) {
|
||||
gen.writeRaw(',');
|
||||
}
|
||||
write(gen, list.get(i));
|
||||
}
|
||||
gen.writeRaw(']');
|
||||
gen.endArray();
|
||||
|
||||
} else {
|
||||
ScalarType scalarType = typeManager.type(value.getClass());
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.ebeaninternal.server.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.stream.BufferRecycleStrategy;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
@@ -22,8 +24,9 @@ import java.util.Map;
|
||||
*/
|
||||
public final class ReadJson implements SpiJsonReader {
|
||||
|
||||
private final JsonStream jsonStream;
|
||||
private final BeanDescriptor<?> rootDesc;
|
||||
private final JsonParser parser;
|
||||
private final JsonReader parser;
|
||||
private final PathStack pathStack;
|
||||
/**
|
||||
* Map of the JsonReadBeanVisitor keyed by path.
|
||||
@@ -38,9 +41,10 @@ public final class ReadJson implements SpiJsonReader {
|
||||
/**
|
||||
* Construct with parser and readOptions.
|
||||
*/
|
||||
public ReadJson(BeanDescriptor<?> desc, JsonParser parser, JsonReadOptions readOptions, Object objectMapper, boolean update) {
|
||||
public ReadJson(BeanDescriptor<?> desc, JsonReader parser, JsonReadOptions readOptions, Object objectMapper, boolean update, JsonStream jsonStream) {
|
||||
this.rootDesc = desc;
|
||||
this.parser = parser;
|
||||
this.jsonStream = jsonStream;
|
||||
this.objectMapper = objectMapper;
|
||||
this.persistenceContext = initPersistenceContext(readOptions);
|
||||
this.loadContext = initLoadContext(desc, readOptions);
|
||||
@@ -54,7 +58,8 @@ public final class ReadJson implements SpiJsonReader {
|
||||
/**
|
||||
* Construct when transferring load context, persistence context, object mapper etc to a new ReadJson instance.
|
||||
*/
|
||||
private ReadJson(JsonParser moreJson, ReadJson source) {
|
||||
private ReadJson(JsonReader moreJson, ReadJson source) {
|
||||
this.jsonStream = source.jsonStream;
|
||||
this.parser = moreJson;
|
||||
this.rootDesc = source.rootDesc;
|
||||
this.pathStack = source.pathStack;
|
||||
@@ -91,13 +96,22 @@ public final class ReadJson implements SpiJsonReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of ReadJson using the existing context but with a new JsonParser.
|
||||
* Return a new instance of ReadJson using the existing context but with a new JsonReader.
|
||||
*/
|
||||
@Override
|
||||
public SpiJsonReader forJson(JsonParser moreJson) {
|
||||
public SpiJsonReader forJson(JsonReader moreJson) {
|
||||
return new ReadJson(moreJson, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiJsonReader forJson(String moreJson) {
|
||||
JsonReader nestedReader = JsonStream.builder()
|
||||
.bufferRecycling(BufferRecycleStrategy.NO_RECYCLING)
|
||||
.build()
|
||||
.reader(moreJson);
|
||||
return new ReadJson(nestedReader, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bean to the persistence context.
|
||||
*/
|
||||
@@ -152,19 +166,19 @@ public final class ReadJson implements SpiJsonReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JsonParser.
|
||||
* Return the JsonReader.
|
||||
*/
|
||||
@Override
|
||||
public JsonParser parser() {
|
||||
public JsonReader parser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next JsonToken from the underlying parser.
|
||||
* Return the current token from the underlying parser.
|
||||
*/
|
||||
@Override
|
||||
public JsonToken nextToken() throws IOException {
|
||||
return parser.nextToken();
|
||||
public Token nextToken() throws IOException {
|
||||
return parser.currentToken();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +223,7 @@ public final class ReadJson implements SpiJsonReader {
|
||||
*/
|
||||
@Override
|
||||
public Object readValueUsingObjectMapper(Class<?> propertyType) throws IOException {
|
||||
return mapper().readValue(parser, propertyType);
|
||||
return mapper().readValue(parser.readRaw(), propertyType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.config.JsonConfig;
|
||||
@@ -24,7 +24,7 @@ import java.util.Set;
|
||||
public final class WriteJson implements SpiJsonWriter {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
private final JsonGenerator generator;
|
||||
private final JsonWriter generator;
|
||||
private final FetchPath fetchPath;
|
||||
private final Map<String, JsonWriteBeanVisitor<?>> visitors;
|
||||
private final PathStack pathStack;
|
||||
@@ -37,7 +37,7 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
/**
|
||||
* Construct for full bean use (normal).
|
||||
*/
|
||||
public WriteJson(SpiEbeanServer server, JsonGenerator generator, FetchPath fetchPath,
|
||||
public WriteJson(SpiEbeanServer server, JsonWriter generator, FetchPath fetchPath,
|
||||
Map<String, JsonWriteBeanVisitor<?>> visitors, Object objectMapper, JsonConfig.Include include,
|
||||
boolean includeLoadedImplicit) {
|
||||
|
||||
@@ -50,12 +50,14 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
this.includeLoadedImplicit = includeLoadedImplicit;
|
||||
this.parentBeans = new ArrayStack<>();
|
||||
this.pathStack = new PathStack();
|
||||
this.generator.serializeNulls(isIncludeNull());
|
||||
this.generator.serializeEmpty(isIncludeEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for Json scalar use.
|
||||
*/
|
||||
public WriteJson(JsonGenerator generator, JsonConfig.Include include) {
|
||||
public WriteJson(JsonWriter generator, JsonConfig.Include include) {
|
||||
this.generator = generator;
|
||||
this.include = include;
|
||||
this.includeLoadedImplicit = true;
|
||||
@@ -65,6 +67,8 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
this.objectMapper = null;
|
||||
this.parentBeans = null;
|
||||
this.pathStack = null;
|
||||
this.generator.serializeNulls(isIncludeNull());
|
||||
this.generator.serializeEmpty(isIncludeEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +88,7 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonGenerator gen() {
|
||||
public JsonWriter gen() {
|
||||
return generator;
|
||||
}
|
||||
|
||||
@@ -95,171 +99,113 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
|
||||
@Override
|
||||
public void writeStartObject(String key) {
|
||||
try {
|
||||
if (key != null) {
|
||||
generator.writeFieldName(key);
|
||||
}
|
||||
generator.writeStartObject();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
if (key != null) {
|
||||
generator.name(key);
|
||||
}
|
||||
generator.beginObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStartObject() {
|
||||
try {
|
||||
generator.writeStartObject();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.beginObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndObject() {
|
||||
try {
|
||||
generator.writeEndObject();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStartArray(String key) {
|
||||
try {
|
||||
if (key != null) {
|
||||
generator.writeFieldName(key);
|
||||
}
|
||||
generator.writeStartArray();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
if (key != null) {
|
||||
generator.name(key);
|
||||
}
|
||||
generator.beginArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStartArray() {
|
||||
try {
|
||||
generator.writeStartArray();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.beginArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndArray() {
|
||||
try {
|
||||
generator.writeEndArray();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.endArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeRaw(String text) {
|
||||
try {
|
||||
generator.writeRaw(text);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.rawChunkStart();
|
||||
generator.rawChunk(text);
|
||||
generator.rawChunkEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeRawValue(String text) {
|
||||
try {
|
||||
generator.writeRawValue(text);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.rawValue(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFieldName(String name) {
|
||||
try {
|
||||
generator.writeFieldName(name);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNullField(String name) {
|
||||
if (isIncludeNull()) {
|
||||
try {
|
||||
generator.writeNullField(name);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.nullValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumberField(String name, long value) {
|
||||
try {
|
||||
generator.writeNumberField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumberField(String name, double value) {
|
||||
try {
|
||||
generator.writeNumberField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumberField(String name, int value) {
|
||||
try {
|
||||
generator.writeNumberField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumberField(String name, short value) {
|
||||
try {
|
||||
generator.writeNumberField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeNumberField(String name, float value) {
|
||||
try {
|
||||
generator.writeNumberField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value((double) value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeNumberField(String name, BigDecimal value) {
|
||||
try {
|
||||
generator.writeNumberField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStringField(String name, String value) {
|
||||
try {
|
||||
generator.writeStringField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBinary(InputStream is, int length) {
|
||||
try {
|
||||
generator.writeBinary(is, length);
|
||||
generator.value(is.readNBytes(length));
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
@@ -267,83 +213,49 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
|
||||
@Override
|
||||
public void writeBinaryField(String name, byte[] value) {
|
||||
try {
|
||||
generator.writeBinaryField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBooleanField(String name, boolean value) {
|
||||
try {
|
||||
generator.writeBooleanField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.name(name);
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(boolean value) {
|
||||
try {
|
||||
generator.writeBoolean(value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(String value) {
|
||||
try {
|
||||
generator.writeString(value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumber(int value) {
|
||||
try {
|
||||
generator.writeNumber(value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumber(long value) {
|
||||
try {
|
||||
generator.writeNumber(value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumber(double value) {
|
||||
try {
|
||||
generator.writeNumber(value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNumber(BigDecimal value) {
|
||||
try {
|
||||
generator.writeNumber(value);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNull() {
|
||||
try {
|
||||
generator.writeNull();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
generator.nullValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -375,55 +287,39 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
|
||||
@Override
|
||||
public void beginAssocMany(String key) {
|
||||
try {
|
||||
pathStack.pushPathKey(key);
|
||||
if (key != null) {
|
||||
generator.writeFieldName(key);
|
||||
}
|
||||
generator.writeStartArray();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
pathStack.pushPathKey(key);
|
||||
if (key != null) {
|
||||
generator.name(key);
|
||||
}
|
||||
generator.beginArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAssocMany() {
|
||||
try {
|
||||
pathStack.pop();
|
||||
generator.writeEndArray();
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
pathStack.pop();
|
||||
generator.endArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginAssocManyMap(String key, boolean elementCollection) {
|
||||
try {
|
||||
pathStack.pushPathKey(key);
|
||||
if (key != null) {
|
||||
generator.writeFieldName(key);
|
||||
}
|
||||
if (elementCollection) {
|
||||
generator.writeStartObject();
|
||||
} else {
|
||||
generator.writeStartArray();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
pathStack.pushPathKey(key);
|
||||
if (key != null) {
|
||||
generator.name(key);
|
||||
}
|
||||
if (elementCollection) {
|
||||
generator.beginObject();
|
||||
} else {
|
||||
generator.beginArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAssocManyMap(boolean elementCollection) {
|
||||
try {
|
||||
pathStack.pop();
|
||||
if (elementCollection) {
|
||||
generator.writeEndObject();
|
||||
} else {
|
||||
generator.writeEndArray();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
pathStack.pop();
|
||||
if (elementCollection) {
|
||||
generator.endObject();
|
||||
} else {
|
||||
generator.endArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,8 +361,8 @@ public final class WriteJson implements SpiJsonWriter {
|
||||
}
|
||||
}
|
||||
try {
|
||||
generator.writeFieldName(name);
|
||||
objectMapper().writeValue(generator, value);
|
||||
generator.name(name);
|
||||
generator.rawValue(objectMapper().writeValueAsString(value));
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
|
||||
+40
-31
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.server.readaudit;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.avaje.applog.AppLog;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.avaje.json.stream.JsonStream;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditQueryPlan;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
@@ -24,7 +24,7 @@ public class DefaultReadAuditLogger implements ReadAuditLogger {
|
||||
private static final System.Logger queryLogger = AppLog.getLogger("io.ebean.ReadAuditQuery");
|
||||
private static final System.Logger auditLogger = AppLog.getLogger("io.ebean.ReadAudit");
|
||||
|
||||
protected final JsonFactory jsonFactory = new JsonFactory();
|
||||
protected final JsonStream jsonStream = JsonStream.builder().build();
|
||||
protected final int defaultQueryBuffer = 500;
|
||||
protected final int defaultReadBuffer = 150;
|
||||
|
||||
@@ -34,25 +34,26 @@ public class DefaultReadAuditLogger implements ReadAuditLogger {
|
||||
@Override
|
||||
public void queryPlan(ReadAuditQueryPlan queryPlan) {
|
||||
StringWriter writer = new StringWriter(defaultQueryBuffer);
|
||||
try (JsonGenerator gen = jsonFactory.createGenerator(writer)) {
|
||||
gen.writeStartObject();
|
||||
try (JsonWriter gen = jsonStream.writer(writer)) {
|
||||
gen.beginObject();
|
||||
String beanType = queryPlan.getBeanType();
|
||||
if (beanType != null) {
|
||||
gen.writeStringField("beanType", beanType);
|
||||
gen.name("beanType");
|
||||
gen.value(beanType);
|
||||
}
|
||||
String queryKey = queryPlan.getQueryKey();
|
||||
if (queryKey != null) {
|
||||
gen.writeStringField("queryKey", queryKey);
|
||||
gen.name("queryKey");
|
||||
gen.value(queryKey);
|
||||
}
|
||||
String sql = queryPlan.getSql();
|
||||
if (sql != null) {
|
||||
gen.writeStringField("sql", sql);
|
||||
gen.name("sql");
|
||||
gen.value(sql);
|
||||
}
|
||||
gen.writeEndObject();
|
||||
gen.endObject();
|
||||
gen.flush();
|
||||
queryLogger.log(INFO, writer.toString());
|
||||
} catch (IOException e) {
|
||||
CoreLog.log.log(ERROR, "Error writing Read audit event", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,56 +74,64 @@ public class DefaultReadAuditLogger implements ReadAuditLogger {
|
||||
}
|
||||
|
||||
protected void writeEvent(ReadEvent event) {
|
||||
try {
|
||||
StringWriter writer = new StringWriter(defaultReadBuffer);
|
||||
JsonGenerator gen = jsonFactory.createGenerator(writer);
|
||||
StringWriter writer = new StringWriter(defaultReadBuffer);
|
||||
try (JsonWriter gen = jsonStream.writer(writer)) {
|
||||
writeDetails(gen, event);
|
||||
auditLogger.log(INFO, writer.toString());
|
||||
} catch (IOException e) {
|
||||
CoreLog.log.log(ERROR, "Error writing Read audit event", e);
|
||||
return;
|
||||
}
|
||||
auditLogger.log(INFO, writer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the details for the read bean or read many beans event.
|
||||
*/
|
||||
protected void writeDetails(JsonGenerator gen, ReadEvent event) throws IOException {
|
||||
gen.writeStartObject();
|
||||
protected void writeDetails(JsonWriter gen, ReadEvent event) throws IOException {
|
||||
gen.beginObject();
|
||||
String source = event.getSource();
|
||||
if (source != null) {
|
||||
gen.writeStringField("source", source);
|
||||
gen.name("source");
|
||||
gen.value(source);
|
||||
}
|
||||
String userId = event.getUserId();
|
||||
if (userId != null) {
|
||||
gen.writeStringField("userId", userId);
|
||||
gen.name("userId");
|
||||
gen.value(userId);
|
||||
}
|
||||
String userIpAddress = event.getUserIpAddress();
|
||||
if (userIpAddress != null) {
|
||||
gen.writeStringField("userIpAddress", userIpAddress);
|
||||
gen.name("userIpAddress");
|
||||
gen.value(userIpAddress);
|
||||
}
|
||||
Map<String, String> userContext = event.getUserContext();
|
||||
if (userContext != null && !userContext.isEmpty()) {
|
||||
gen.writeObjectFieldStart("userContext");
|
||||
gen.name("userContext");
|
||||
gen.beginObject();
|
||||
for (Map.Entry<String, String> entry : userContext.entrySet()) {
|
||||
gen.writeStringField(entry.getKey(), entry.getValue());
|
||||
gen.name(entry.getKey());
|
||||
gen.value(entry.getValue());
|
||||
}
|
||||
gen.writeEndObject();
|
||||
gen.endObject();
|
||||
}
|
||||
gen.writeNumberField("eventTime", event.getEventTime());
|
||||
gen.writeStringField("beanType", event.getBeanType());
|
||||
gen.writeStringField("queryKey", event.getQueryKey());
|
||||
gen.writeStringField("bindLog", event.getBindLog());
|
||||
gen.name("eventTime");
|
||||
gen.value(event.getEventTime());
|
||||
gen.name("beanType");
|
||||
gen.value(event.getBeanType());
|
||||
gen.name("queryKey");
|
||||
gen.value(event.getQueryKey());
|
||||
gen.name("bindLog");
|
||||
gen.value(event.getBindLog());
|
||||
Object id = event.getId();
|
||||
if (id != null) {
|
||||
gen.writeFieldName("id");
|
||||
gen.name("id");
|
||||
EJson.write(id, gen);
|
||||
} else {
|
||||
gen.writeFieldName("ids");
|
||||
gen.name("ids");
|
||||
EJson.writeCollection(event.getIds(), gen);
|
||||
}
|
||||
gen.writeEndObject();
|
||||
gen.endObject();
|
||||
gen.flush();
|
||||
gen.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -319,7 +319,11 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
if (type.equals(Set.class) && isValueTypeSimple(genericType)) {
|
||||
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), keepSource(prop));
|
||||
}
|
||||
if (type.equals(Map.class) && isMapValueTypeObject(genericType)) {
|
||||
if (type.equals(Map.class) && isBuiltinJsonMap(genericType)) {
|
||||
Type keyType = TypeReflectHelper.getMapKeyTypeRaw(genericType);
|
||||
if (isEnumType(keyType)) {
|
||||
return enumJsonMapType(postgres, dbType, keyType, keepSource(prop));
|
||||
}
|
||||
return ScalarTypeJsonMap.typeFor(postgres, dbType, keepSource(prop));
|
||||
}
|
||||
if (objectMapperPresent && prop.getMutationDetection() == MutationDetection.DEFAULT) {
|
||||
@@ -338,6 +342,13 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
return prop.getMutationDetection() == MutationDetection.SOURCE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ScalarType<?> enumJsonMapType(boolean postgres, int dbType, Type keyType, boolean keepSource) {
|
||||
Class<? extends Enum<?>> enumClass = asEnumClass(keyType);
|
||||
ScalarType<? extends Enum<?>> enumScalarType = (ScalarType<? extends Enum<?>>) enumType(enumClass, null);
|
||||
return ScalarTypeJsonMapEnum.typeFor(postgres, dbType, enumScalarType, keepSource);
|
||||
}
|
||||
|
||||
private DocPropertyType docPropertyType(DeployBeanProperty prop, Class<?> type) {
|
||||
return type.equals(List.class) || type.equals(Set.class) ? docType(prop.getGenericType()) : DocPropertyType.OBJECT;
|
||||
}
|
||||
@@ -365,11 +376,24 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if value parameter type of the map is Object.
|
||||
* Return true if the Map is handled by the built-in JSON support: a String or enum key
|
||||
* with a String, Object or wildcard value. Such values round-trip through EJson without
|
||||
* type coercion. Other maps (typed values, or non-String/enum keys) use the object mapper.
|
||||
*/
|
||||
private boolean isMapValueTypeObject(Type genericType) {
|
||||
private boolean isBuiltinJsonMap(Type genericType) {
|
||||
if (!(genericType instanceof ParameterizedType)) {
|
||||
return false;
|
||||
}
|
||||
Type[] typeArgs = ((ParameterizedType) genericType).getActualTypeArguments();
|
||||
return Object.class.equals(typeArgs[1]) || "?".equals(typeArgs[1].toString());
|
||||
return isJsonMapKeyType(typeArgs[0]) && isJsonMapValueType(typeArgs[1]);
|
||||
}
|
||||
|
||||
private boolean isJsonMapKeyType(Type keyType) {
|
||||
return String.class.equals(keyType) || isEnumType(keyType);
|
||||
}
|
||||
|
||||
private boolean isJsonMapValueType(Type valueType) {
|
||||
return Object.class.equals(valueType) || String.class.equals(valueType) || "?".equals(valueType.toString());
|
||||
}
|
||||
|
||||
private ScalarType<?> createJsonObjectMapperType(DeployBeanProperty prop, int dbType, DocPropertyType docType) {
|
||||
@@ -560,6 +584,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
*/
|
||||
private ScalarTypeEnum<?> enumTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType, int length, boolean withConstraint) {
|
||||
Map<String, String> nameValueMap = new LinkedHashMap<>();
|
||||
method.setAccessible(true);
|
||||
for (Enum<?> enumConstant : enumType.getEnumConstants()) {
|
||||
try {
|
||||
Object value = method.invoke(enumConstant);
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.PostgresHelper;
|
||||
import io.ebean.util.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Strategy for reading and binding the raw JSON string of a {@code @DbJson} property.
|
||||
* <p>
|
||||
* This collapses the previously duplicated VARCHAR / CLOB / BLOB and Postgres JSON / JSONB
|
||||
* variants (which differed only in how the raw JSON is read from / bound to JDBC) into a
|
||||
* small set of reusable strategies shared by all JSON value types (Map, List, Set).
|
||||
*/
|
||||
interface JsonStorage {
|
||||
|
||||
/** VARCHAR storage - read/bind as a String. */
|
||||
JsonStorage VARCHAR = new Varchar();
|
||||
/** CLOB storage - read via stream, bind as a String. */
|
||||
JsonStorage CLOB = new Clob();
|
||||
/** BLOB storage - read/bind as UTF-8 bytes. */
|
||||
JsonStorage BLOB = new Blob();
|
||||
|
||||
/** Postgres JSON / JSONB storage binding via a PGobject of the given type. */
|
||||
static JsonStorage postgres(String pgType) {
|
||||
return new Postgres(pgType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the raw JSON content (or null) from the DB.
|
||||
*/
|
||||
String read(DataReader reader) throws SQLException;
|
||||
|
||||
/**
|
||||
* The JDBC type reported by a ScalarType using this storage.
|
||||
*/
|
||||
int jdbcType();
|
||||
|
||||
/**
|
||||
* Bind the given non-null raw JSON content.
|
||||
*/
|
||||
void bind(DataBinder binder, String rawJson) throws SQLException;
|
||||
|
||||
/**
|
||||
* Bind an SQL null value.
|
||||
*/
|
||||
void bindNull(DataBinder binder) throws SQLException;
|
||||
|
||||
final class Varchar implements JsonStorage {
|
||||
@Override
|
||||
public String read(DataReader reader) throws SQLException {
|
||||
return reader.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int jdbcType() {
|
||||
return Types.VARCHAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
final class Clob implements JsonStorage {
|
||||
@Override
|
||||
public String read(DataReader reader) throws SQLException {
|
||||
return reader.getStringFromStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int jdbcType() {
|
||||
return Types.CLOB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
final class Blob implements JsonStorage {
|
||||
@Override
|
||||
public String read(DataReader reader) throws SQLException {
|
||||
InputStream is = reader.getBinaryStream();
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
try (Reader streamReader = IOUtils.newReader(is)) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
char[] buffer = new char[2048];
|
||||
int nRead;
|
||||
while ((nRead = streamReader.read(buffer)) >= 0) {
|
||||
builder.append(buffer, 0, nRead);
|
||||
}
|
||||
return builder.toString();
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading Blob stream from DB", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int jdbcType() {
|
||||
return Types.BLOB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.BLOB);
|
||||
}
|
||||
}
|
||||
|
||||
final class Postgres implements JsonStorage {
|
||||
private final String pgType;
|
||||
private final int jdbcType;
|
||||
|
||||
Postgres(String pgType) {
|
||||
this.pgType = pgType;
|
||||
this.jdbcType = PostgresHelper.JSONB_TYPE.equals(pgType) ? DbPlatformType.JSONB : DbPlatformType.JSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String read(DataReader reader) throws SQLException {
|
||||
return reader.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int jdbcType() {
|
||||
return jdbcType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, rawJson));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -30,7 +30,7 @@ class PlatformArrayTypeJsonList implements PlatformArrayTypeFactory {
|
||||
// TODO: keepSource for @DbArray?
|
||||
return new ScalarTypeJsonList.VarcharWithConverter(DocPropertyType.UUID, nullable, false, ArrayElementConverter.UUID);
|
||||
}
|
||||
return new ScalarTypeJsonList.Varchar(docType(valueType), nullable, false);
|
||||
return new ScalarTypeJsonList(java.sql.Types.VARCHAR, JsonStorage.VARCHAR, docType(valueType), nullable, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,7 +30,7 @@ class PlatformArrayTypeJsonSet implements PlatformArrayTypeFactory {
|
||||
// TODO: keepSource for @DbArray?
|
||||
return new ScalarTypeJsonSet.VarcharWithConverter(DocPropertyType.UUID, nullable, false, ArrayElementConverter.UUID);
|
||||
}
|
||||
return new ScalarTypeJsonSet.Varchar(docType(valueType), nullable, false);
|
||||
return new ScalarTypeJsonSet(java.sql.Types.VARCHAR, JsonStorage.VARCHAR, docType(valueType), nullable, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
@@ -173,12 +173,16 @@ class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements ScalarTyp
|
||||
}
|
||||
|
||||
@Override
|
||||
public List jsonRead(JsonParser parser) throws IOException {
|
||||
return convert(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
public List jsonRead(JsonReader parser) throws IOException {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
List<Object> rawList = EJson.parseList(parser, parser.currentToken());
|
||||
return convert(rawList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, List value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, List value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
@@ -152,12 +152,16 @@ class ScalarTypeArraySet extends ScalarTypeArrayBase<Set> implements ScalarTypeA
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set jsonRead(JsonParser parser) throws IOException {
|
||||
return convert(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
public Set jsonRead(JsonReader parser) throws IOException {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
List<Object> rawList = EJson.parseList(parser, parser.currentToken());
|
||||
return convert(rawList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Set value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, Set value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -78,13 +78,13 @@ class ScalarTypeBigDecimal extends ScalarTypeBase<BigDecimal> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getDecimalValue();
|
||||
public BigDecimal jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readDecimal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, BigDecimal value) throws IOException {
|
||||
writer.writeNumber(value);
|
||||
public void jsonWrite(JsonWriter writer, BigDecimal value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -348,13 +347,39 @@ public final class ScalarTypeBoolean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean jsonRead(JsonParser parser) {
|
||||
return JsonToken.VALUE_TRUE == parser.getCurrentToken() ? Boolean.TRUE : Boolean.FALSE;
|
||||
public Boolean jsonRead(JsonReader parser) {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
var token = parser.currentToken();
|
||||
if (token == JsonReader.Token.BOOLEAN) {
|
||||
return parser.readBoolean();
|
||||
}
|
||||
if (token == JsonReader.Token.NUMBER) {
|
||||
return parser.readDecimal().intValue() == 1;
|
||||
}
|
||||
if (token == JsonReader.Token.STRING) {
|
||||
return parse(parser.readString());
|
||||
}
|
||||
String raw = parser.readRaw();
|
||||
if (raw == null || "null".equals(raw)) {
|
||||
return null;
|
||||
}
|
||||
if ("true".equals(raw) || "1".equals(raw)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if ("false".equals(raw) || "0".equals(raw)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
if (raw.length() > 1 && raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') {
|
||||
return parse(raw.substring(1, raw.length() - 1));
|
||||
}
|
||||
return parse(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Boolean value) throws IOException {
|
||||
writer.writeBoolean(value);
|
||||
public void jsonWrite(JsonWriter writer, Boolean value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -9,7 +9,6 @@ import io.ebean.core.type.ScalarTypeBase;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.core.type.BasicTypeConverter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
@@ -55,15 +54,13 @@ final class ScalarTypeByte extends ScalarTypeBase<Byte> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Byte value) throws IOException {
|
||||
writer.writeBinary(new byte[]{value});
|
||||
public void jsonWrite(JsonWriter writer, Byte value) throws IOException {
|
||||
writer.value(new byte[]{value});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte jsonRead(JsonParser parser) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
parser.readBinaryValue(os);
|
||||
byte[] bytes = os.toByteArray();
|
||||
public Byte jsonRead(JsonReader parser) throws IOException {
|
||||
byte[] bytes = parser.readBinary();
|
||||
if (bytes.length == 0) {
|
||||
return null;
|
||||
} else {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarTypeBase;
|
||||
import io.ebean.text.TextException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
@@ -85,15 +84,13 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase<byte[]> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, byte[] value) throws IOException {
|
||||
writer.writeBinary(value);
|
||||
public void jsonWrite(JsonWriter writer, byte[] value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] jsonRead(JsonParser parser) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(500);
|
||||
parser.readBinaryValue(out);
|
||||
return out.toByteArray();
|
||||
public byte[] jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readBinary();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
@@ -58,15 +57,13 @@ public final class ScalarTypeBytesEncrypted implements ScalarType<byte[]> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, byte[] value) throws IOException {
|
||||
writer.writeBinary(value);
|
||||
public void jsonWrite(JsonWriter writer, byte[] value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] jsonRead(JsonParser parser) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(500);
|
||||
parser.readBinaryValue(out);
|
||||
return out.toByteArray();
|
||||
public byte[] jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readBinary();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarTypeBaseVarchar;
|
||||
@@ -72,7 +72,7 @@ final class ScalarTypeCharArray extends ScalarTypeBaseVarchar<char[]> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getValueAsString().toCharArray();
|
||||
public char[] jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readString().toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -77,13 +77,13 @@ final class ScalarTypeDouble extends ScalarTypeBase<Double> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getDoubleValue();
|
||||
public Double jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Double value) throws IOException {
|
||||
writer.writeNumber(value);
|
||||
public void jsonWrite(JsonWriter writer, Double value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.*;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -90,13 +90,13 @@ class ScalarTypeDuration extends ScalarTypeBase<Duration> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration jsonRead(JsonParser parser) throws IOException {
|
||||
return Duration.parse(parser.getValueAsString());
|
||||
public Duration jsonRead(JsonReader parser) throws IOException {
|
||||
return Duration.parse(parser.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Duration value) throws IOException {
|
||||
writer.writeString(value.toString());
|
||||
public void jsonWrite(JsonWriter writer, Duration value) throws IOException {
|
||||
writer.value(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -120,12 +120,12 @@ public final class ScalarTypeEncryptedWrapper<T> implements ScalarType<T>, Local
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser) throws IOException {
|
||||
public T jsonRead(JsonReader parser) throws IOException {
|
||||
return wrapped.jsonRead(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, T value) throws IOException {
|
||||
wrapped.jsonWrite(writer, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarTypeBase;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
|
||||
import jakarta.persistence.EnumType;
|
||||
import java.io.DataInput;
|
||||
@@ -228,20 +228,19 @@ final class ScalarTypeEnumStandard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonRead(JsonParser parser) throws IOException {
|
||||
if (parser.getCodec() != null) {
|
||||
return parser.readValueAs(enumType);
|
||||
} else {
|
||||
return parse(parser.getValueAsString());
|
||||
public Object jsonRead(JsonReader parser) throws IOException {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
return parse(parser.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Object value) throws IOException {
|
||||
if (writer.getCodec() != null) {
|
||||
writer.writeObject(value);
|
||||
public void jsonWrite(JsonWriter writer, Object value) throws IOException {
|
||||
if (value == null) {
|
||||
writer.nullValue();
|
||||
} else {
|
||||
writer.writeString(formatValue(value));
|
||||
writer.value(formatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -102,16 +102,17 @@ final class ScalarTypeFile extends ScalarTypeBase<File> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, File value) throws IOException {
|
||||
InputStream is = getInputStream(value);
|
||||
writer.writeBinary(is, (int) value.length());
|
||||
public void jsonWrite(JsonWriter writer, File value) throws IOException {
|
||||
try (InputStream is = getInputStream(value)) {
|
||||
writer.value(is.readAllBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File jsonRead(JsonParser parser) throws IOException {
|
||||
public File jsonRead(JsonReader parser) throws IOException {
|
||||
File tempFile = File.createTempFile(prefix, suffix, directory);
|
||||
try (OutputStream os = getOutputStream(tempFile)) {
|
||||
parser.readBinaryValue(os);
|
||||
os.write(parser.readBinary());
|
||||
os.flush();
|
||||
}
|
||||
return tempFile;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -77,13 +77,13 @@ final class ScalarTypeFloat extends ScalarTypeBase<Float> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getFloatValue();
|
||||
public Float jsonRead(JsonReader parser) throws IOException {
|
||||
return (float) parser.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Float value) throws IOException {
|
||||
writer.writeNumber(value);
|
||||
public void jsonWrite(JsonWriter writer, Float value) throws IOException {
|
||||
writer.value(value.doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -87,13 +87,13 @@ final class ScalarTypeInteger extends ScalarTypeBase<Integer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getIntValue();
|
||||
public Integer jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Integer value) throws IOException {
|
||||
writer.writeNumber(value);
|
||||
public void jsonWrite(JsonWriter writer, Integer value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
|
||||
/**
|
||||
* Base for the JSON collection value types (List, Set).
|
||||
* <p>
|
||||
* Adds the {@link ScalarTypeArray} aspect (logical DB array column definition) used when the
|
||||
* same type backs a {@code @DbArray} mapping on a platform without native array support.
|
||||
*/
|
||||
abstract class ScalarTypeJsonCollectionValue<T> extends ScalarTypeJsonValue<T> implements ScalarTypeArray {
|
||||
|
||||
ScalarTypeJsonCollectionValue(Class<T> type, int jdbcType, JsonStorage storage, boolean keepSource,
|
||||
boolean nullable, DocPropertyType docType) {
|
||||
super(type, jdbcType, storage, keepSource, nullable, "[]", docType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getDbColumnDefn() {
|
||||
switch (docType()) {
|
||||
case SHORT:
|
||||
case INTEGER:
|
||||
case LONG:
|
||||
return "integer[]";
|
||||
case DOUBLE:
|
||||
case FLOAT:
|
||||
return "decimal[]";
|
||||
default:
|
||||
return "varchar[]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,218 +1,122 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.*;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.PostgresHelper;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.json.ModifyAwareList;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Types for mapping List in JSON format to DB types VARCHAR, JSON and JSONB.
|
||||
* Type which maps a List in JSON format to VARCHAR or Postgres JSON / JSONB.
|
||||
*/
|
||||
final class ScalarTypeJsonList {
|
||||
@SuppressWarnings("rawtypes")
|
||||
class ScalarTypeJsonList extends ScalarTypeJsonCollectionValue<List> {
|
||||
|
||||
/**
|
||||
* Return the appropriate ScalarType based requested dbType and if Postgres.
|
||||
* Return the appropriate ScalarType for the requested dbType and platform.
|
||||
*/
|
||||
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbPlatformType.JSONB:
|
||||
return new ScalarTypeJsonList.JsonB(docType, nullable, keepSource);
|
||||
return new ScalarTypeJsonList(DbPlatformType.JSONB, JsonStorage.postgres(PostgresHelper.JSONB_TYPE), docType, nullable, keepSource);
|
||||
case DbPlatformType.JSON:
|
||||
return new ScalarTypeJsonList.Json(docType, nullable, keepSource);
|
||||
return new ScalarTypeJsonList(DbPlatformType.JSON, JsonStorage.postgres(PostgresHelper.JSON_TYPE), docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
return new ScalarTypeJsonList.Varchar(docType, nullable, keepSource);
|
||||
return new ScalarTypeJsonList(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final class VarcharWithConverter extends ScalarTypeJsonList.Base {
|
||||
ScalarTypeJsonList(int jdbcType, JsonStorage storage, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(List.class, jdbcType, storage, keepSource, nullable, docType);
|
||||
}
|
||||
|
||||
@Override
|
||||
List readJson(String rawJson) {
|
||||
try {
|
||||
// parse JSON into a modifyAware list
|
||||
return EJson.parseList(rawJson, true);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", rawJson, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(List value) {
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to format List into JSON content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List jsonRead(JsonReader parser) throws IOException {
|
||||
return EJson.parseList(parser, parser.currentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonWriter writer, List value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to VARCHAR with element conversion - used as the {@code @DbArray} fallback
|
||||
* on platforms without native array support.
|
||||
*/
|
||||
static final class VarcharWithConverter extends ScalarTypeJsonList {
|
||||
|
||||
private final ArrayElementConverter converter;
|
||||
|
||||
VarcharWithConverter(DocPropertyType docType, boolean nullable, boolean keepSource, ArrayElementConverter converter) {
|
||||
super(Types.VARCHAR, docType, nullable, keepSource);
|
||||
super(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
List readJsonConvert(String json) {
|
||||
try {
|
||||
return convertElements(EJson.parseList(json, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", json, e);
|
||||
}
|
||||
List readJson(String rawJson) {
|
||||
return convert(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
return convert(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List convertElements(List<Object> rawList) {
|
||||
if (rawList == null) {
|
||||
return null;
|
||||
}
|
||||
final List result = new ArrayList<>(rawList.size());
|
||||
for (Object o : rawList) {
|
||||
result.add(converter.fromSerialized(o));
|
||||
}
|
||||
return new ModifyAwareList(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
private List convert(String json) {
|
||||
try {
|
||||
return convertElements(EJson.parseList(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", value, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
static final class Varchar extends ScalarTypeJsonList.Base {
|
||||
Varchar(DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(Types.VARCHAR, docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSON.
|
||||
*/
|
||||
private final static class Json extends ScalarTypeJsonList.PgBase {
|
||||
Json(DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSONB.
|
||||
*/
|
||||
private static final class JsonB extends ScalarTypeJsonList.PgBase {
|
||||
JsonB(DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for List handling.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private abstract static class Base extends ScalarTypeJsonCollection<List> {
|
||||
final boolean keepSource;
|
||||
|
||||
private Base(int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(List.class, dbType, docType, nullable);
|
||||
this.keepSource = keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean jsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List read(DataReader reader) throws SQLException {
|
||||
String json = reader.getString();
|
||||
if (keepSource) {
|
||||
reader.pushJson(json);
|
||||
}
|
||||
return readJsonConvert(json);
|
||||
}
|
||||
|
||||
List readJsonConvert(String json) {
|
||||
try {
|
||||
// parse JSON into modifyAware list
|
||||
return EJson.parseList(json, true);
|
||||
List<Object> rawList = EJson.parseList(json, false);
|
||||
if (rawList == null) {
|
||||
return null;
|
||||
}
|
||||
final List result = new ArrayList<>(rawList.size());
|
||||
for (Object o : rawList) {
|
||||
result.add(converter.fromSerialized(o));
|
||||
}
|
||||
return new ModifyAwareList(result);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", json, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(DataBinder binder, List value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else {
|
||||
bindRawJson(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
if (nullable) {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
} else {
|
||||
binder.setString("[]");
|
||||
}
|
||||
}
|
||||
|
||||
protected void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String formatValue(List value) {
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to format List into JSON content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseList(parser, parser.getCurrentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void jsonWrite(JsonGenerator writer, List value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Postgres extension to base List handling.
|
||||
*/
|
||||
private static class PgBase extends ScalarTypeJsonList.Base {
|
||||
|
||||
final String pgType;
|
||||
|
||||
PgBase(int jdbcType, String pgType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(jdbcType, docType, nullable, keepSource);
|
||||
this.pgType = pgType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, rawJson));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, nullable ? null : "[]"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,204 +1,63 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarTypeBase;
|
||||
import io.ebean.core.type.PostgresHelper;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebean.util.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.io.IOException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Type which maps Map<String,Object> to various DB types (Clob, Varchar, Blob) in JSON format.
|
||||
* Type which maps {@code Map<String,Object>} to JSON stored in VARCHAR, CLOB, BLOB,
|
||||
* or Postgres JSON / JSONB.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
class ScalarTypeJsonMap extends ScalarTypeJsonValue<Map> {
|
||||
|
||||
/**
|
||||
* Return the ScalarType for the requested dbType and postgres.
|
||||
* Return the ScalarType for the requested dbType and platform.
|
||||
*/
|
||||
static ScalarTypeJsonMap typeFor(boolean postgres, int dbType, boolean keepSource) {
|
||||
return new ScalarTypeJsonMap(storageFor(postgres, dbType), keepSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the storage strategy for the given dbType and platform. Shared with the
|
||||
* enum-key Map variant.
|
||||
*/
|
||||
static JsonStorage storageFor(boolean postgres, int dbType) {
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR:
|
||||
return new ScalarTypeJsonMap.Varchar(keepSource);
|
||||
return JsonStorage.VARCHAR;
|
||||
case Types.BLOB:
|
||||
return new ScalarTypeJsonMap.Blob(keepSource);
|
||||
return JsonStorage.BLOB;
|
||||
case Types.CLOB:
|
||||
return new ScalarTypeJsonMap.Clob(keepSource);
|
||||
return JsonStorage.CLOB;
|
||||
case DbPlatformType.JSONB:
|
||||
return postgres ? new ScalarTypeJsonMapPostgres.JSONB(keepSource) : new ScalarTypeJsonMap.Clob(keepSource);
|
||||
return postgres ? JsonStorage.postgres(PostgresHelper.JSONB_TYPE) : JsonStorage.CLOB;
|
||||
case DbPlatformType.JSON:
|
||||
return postgres ? new ScalarTypeJsonMapPostgres.JSON(keepSource) : new ScalarTypeJsonMap.Clob(keepSource);
|
||||
return postgres ? JsonStorage.postgres(PostgresHelper.JSON_TYPE) : JsonStorage.CLOB;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown dbType " + dbType);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Clob extends ScalarTypeJsonMap {
|
||||
Clob(boolean keepSource) {
|
||||
super(Types.CLOB, keepSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String readJson(DataReader reader) throws SQLException {
|
||||
return reader.getStringFromStream();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Varchar extends ScalarTypeJsonMap {
|
||||
Varchar(boolean keepSource) {
|
||||
super(Types.VARCHAR, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Blob extends ScalarTypeJsonMap {
|
||||
Blob(boolean keepSource) {
|
||||
super(Types.BLOB, keepSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map read(DataReader reader) throws SQLException {
|
||||
InputStream is = reader.getBinaryStream();
|
||||
if (is == null) {
|
||||
if (keepSource) {
|
||||
reader.pushJson(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (keepSource) {
|
||||
StringWriter jsonBuffer = new StringWriter();
|
||||
try (Reader streamReader = IOUtils.newReader(is)) {
|
||||
transferTo(streamReader, jsonBuffer);
|
||||
}
|
||||
String rawJson = jsonBuffer.toString();
|
||||
reader.pushJson(rawJson);
|
||||
return parse(rawJson);
|
||||
} else {
|
||||
try (Reader streamReader = IOUtils.newReader(is)) {
|
||||
return parse(streamReader);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading Blob stream from DB", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void transferTo(Reader reader, Writer out) throws IOException {
|
||||
char[] buffer = new char[2048];
|
||||
int nRead;
|
||||
while ((nRead = reader.read(buffer, 0, 2048)) >= 0) {
|
||||
out.write(buffer, 0, nRead);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.BLOB);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final boolean keepSource;
|
||||
|
||||
ScalarTypeJsonMap(int jdbcType, boolean keepSource) {
|
||||
super(Map.class, false, jdbcType);
|
||||
this.keepSource = keepSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map is a mutable type. Use the isDirty() method to check for dirty state.
|
||||
*/
|
||||
@Override
|
||||
public final boolean mutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the value should be considered dirty (and included in an update).
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return TypeJsonManager.checkIsDirty(value);
|
||||
ScalarTypeJsonMap(JsonStorage storage, boolean keepSource) {
|
||||
super(Map.class, storage.jdbcType(), storage, keepSource, true, null, DocPropertyType.OBJECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean jsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map read(DataReader reader) throws SQLException {
|
||||
String rawJson = readJson(reader);
|
||||
if (keepSource) {
|
||||
reader.pushJson(rawJson);
|
||||
}
|
||||
if (rawJson == null) {
|
||||
return null;
|
||||
}
|
||||
Map readJson(String rawJson) {
|
||||
return parse(rawJson);
|
||||
}
|
||||
|
||||
protected String readJson(DataReader reader) throws SQLException {
|
||||
return reader.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(DataBinder binder, Map value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else {
|
||||
bindJson(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
}
|
||||
|
||||
protected void bindJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map toBeanType(Object value) {
|
||||
return (Map) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String formatValue(Map v) {
|
||||
try {
|
||||
return EJson.write(v);
|
||||
} catch (IOException e) {
|
||||
throw new TextException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map parse(String value) {
|
||||
public Map parse(String value) {
|
||||
try {
|
||||
// return a modify aware map
|
||||
return EJson.parseObject(value, true);
|
||||
@@ -207,46 +66,22 @@ abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
}
|
||||
}
|
||||
|
||||
public final Map parse(Reader reader) {
|
||||
@Override
|
||||
public String formatValue(Map value) {
|
||||
try {
|
||||
// return a modify aware map
|
||||
return EJson.parseObject(reader, true);
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new TextException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
} else {
|
||||
return parse(dataInput.readUTF());
|
||||
}
|
||||
public Map jsonRead(JsonReader parser) throws IOException {
|
||||
return EJson.parseObject(parser, parser.currentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeData(DataOutput dataOutput, Map map) throws IOException {
|
||||
if (map == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
ScalarHelp.writeUTF(dataOutput, format(map));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void jsonWrite(JsonGenerator writer, Map value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, Map value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseObject(parser, parser.getCurrentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DocPropertyType docType() {
|
||||
return DocPropertyType.OBJECT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.json.ModifyAwareMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Type which maps {@code Map<Enum,Object>} to JSON.
|
||||
* <p>
|
||||
* The enum keys are (de)serialized via their {@link ScalarType} (honouring custom
|
||||
* {@code @DbEnumValue} mappings), then delegated to {@link ScalarTypeJsonMap} for all
|
||||
* storage, platform and value handling.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
final class ScalarTypeJsonMapEnum<T extends Enum<T>> extends ScalarTypeJsonMap {
|
||||
|
||||
private final ScalarType<T> enumType;
|
||||
|
||||
static ScalarType<?> typeFor(boolean postgres, int dbType, ScalarType<? extends Enum<?>> enumType, boolean keepSource) {
|
||||
return new ScalarTypeJsonMapEnum<>(storageFor(postgres, dbType), enumType, keepSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ScalarTypeJsonMapEnum(JsonStorage storage, ScalarType<? extends Enum> enumType, boolean keepSource) {
|
||||
super(storage, keepSource);
|
||||
this.enumType = (ScalarType<T>) enumType;
|
||||
}
|
||||
|
||||
@Override
|
||||
Map readJson(String rawJson) {
|
||||
return parse(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map parse(String value) {
|
||||
try {
|
||||
return toEnumKeys(EJson.parseObject(value, true));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as Map with enum keys", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Map value) {
|
||||
try {
|
||||
return EJson.write(toStringKeys(value));
|
||||
} catch (IOException e) {
|
||||
throw new TextException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map jsonRead(JsonReader parser) throws IOException {
|
||||
return toEnumKeys(EJson.parseObject(parser, parser.currentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonWriter writer, Map value) throws IOException {
|
||||
EJson.write(toStringKeys(value), writer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> toStringKeys(Map value) {
|
||||
Map<String, Object> stringKeyMap = new LinkedHashMap<>();
|
||||
for (Object o : value.entrySet()) {
|
||||
Map.Entry e = (Map.Entry) o;
|
||||
stringKeyMap.put(enumType.formatValue((T) e.getKey()), e.getValue());
|
||||
}
|
||||
return stringKeyMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map toEnumKeys(Map<String, Object> stringKeyMap) {
|
||||
if (stringKeyMap == null) {
|
||||
return null;
|
||||
}
|
||||
Map enumKeyMap = new LinkedHashMap();
|
||||
for (Map.Entry<String, Object> e : stringKeyMap.entrySet()) {
|
||||
enumKeyMap.put(enumType.parse(e.getKey()), e.getValue());
|
||||
}
|
||||
return stringKeyMap instanceof ModifyAwareMap ? new ModifyAwareMap(enumKeyMap) : enumKeyMap;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.PostgresHelper;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Support for the Postgres DB types JSON and JSONB.
|
||||
*/
|
||||
abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
|
||||
private final String postgresType;
|
||||
|
||||
ScalarTypeJsonMapPostgres(int jdbcType, String postgresType, boolean keepSource) {
|
||||
super(jdbcType, keepSource);
|
||||
this.postgresType = postgresType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(postgresType, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(postgresType, rawJson));
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalarType mapping java Map type to Postgres JSON database type.
|
||||
*/
|
||||
static final class JSON extends ScalarTypeJsonMapPostgres {
|
||||
|
||||
JSON(boolean keepSource) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalarType mapping java Map type to Postgres JSONB database type.
|
||||
*/
|
||||
static final class JSONB extends ScalarTypeJsonMapPostgres {
|
||||
|
||||
JSONB(boolean keepSource) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, keepSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +1,124 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.*;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.PostgresHelper;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.json.ModifyAwareSet;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Types for mapping List in JSON format to DB types VARCHAR, JSON and JSONB.
|
||||
* Type which maps a Set in JSON format to VARCHAR or Postgres JSON / JSONB.
|
||||
*/
|
||||
final class ScalarTypeJsonSet {
|
||||
@SuppressWarnings("rawtypes")
|
||||
class ScalarTypeJsonSet extends ScalarTypeJsonCollectionValue<Set> {
|
||||
|
||||
/**
|
||||
* Return the appropriate ScalarType for the requested dbType and Postgres.
|
||||
* Return the appropriate ScalarType for the requested dbType and platform.
|
||||
*/
|
||||
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbPlatformType.JSONB:
|
||||
return new ScalarTypeJsonSet.JsonB(docPropertyType, nullable, keepSource);
|
||||
return new ScalarTypeJsonSet(DbPlatformType.JSONB, JsonStorage.postgres(PostgresHelper.JSONB_TYPE), docType, nullable, keepSource);
|
||||
case DbPlatformType.JSON:
|
||||
return new ScalarTypeJsonSet.Json(docPropertyType, nullable, keepSource);
|
||||
return new ScalarTypeJsonSet(DbPlatformType.JSON, JsonStorage.postgres(PostgresHelper.JSON_TYPE), docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
return new ScalarTypeJsonSet.Varchar(docPropertyType, nullable, keepSource);
|
||||
return new ScalarTypeJsonSet(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final class VarcharWithConverter extends ScalarTypeJsonSet.Base {
|
||||
ScalarTypeJsonSet(int jdbcType, JsonStorage storage, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(Set.class, jdbcType, storage, keepSource, nullable, docType);
|
||||
}
|
||||
|
||||
@Override
|
||||
Set readJson(String rawJson) {
|
||||
try {
|
||||
// parse JSON into a modifyAware set
|
||||
return EJson.parseSet(rawJson, true);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as Set", rawJson, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return new LinkedHashSet(EJson.parseList(value));
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to parse JSON content as Set: " + value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Set value) {
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to format Set into JSON content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set jsonRead(JsonReader parser) throws IOException {
|
||||
return new LinkedHashSet(EJson.parseList(parser, parser.currentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonWriter writer, Set value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mapped to VARCHAR with element conversion - used as the {@code @DbArray} fallback
|
||||
* on platforms without native array support.
|
||||
*/
|
||||
static final class VarcharWithConverter extends ScalarTypeJsonSet {
|
||||
|
||||
private final ArrayElementConverter converter;
|
||||
|
||||
VarcharWithConverter(DocPropertyType docType, boolean nullable, boolean keepSource, ArrayElementConverter converter) {
|
||||
super(Types.VARCHAR, docType, nullable, keepSource);
|
||||
super(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
Set readJsonConvert(String json) {
|
||||
try {
|
||||
return convertElements(EJson.parseSet(json, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", json, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set convertElements(Set<Object> rawSet) {
|
||||
if (rawSet == null) {
|
||||
return null;
|
||||
}
|
||||
final Set result = new LinkedHashSet(rawSet.size());
|
||||
for (Object o : rawSet) {
|
||||
result.add(converter.fromSerialized(o));
|
||||
}
|
||||
return new ModifyAwareSet(result);
|
||||
Set readJson(String rawJson) {
|
||||
return convert(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
return convert(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set convert(String json) {
|
||||
try {
|
||||
return convertElements(EJson.parseSet(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to parse JSON content as Set: " + value, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
static final class Varchar extends ScalarTypeJsonSet.Base {
|
||||
public Varchar(DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(Types.VARCHAR, docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSON.
|
||||
*/
|
||||
private static final class Json extends ScalarTypeJsonSet.PgBase {
|
||||
private Json(DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSONB.
|
||||
*/
|
||||
private static final class JsonB extends ScalarTypeJsonSet.PgBase {
|
||||
private JsonB(DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private abstract static class Base extends ScalarTypeJsonCollection<Set> {
|
||||
|
||||
final boolean keepSource;
|
||||
|
||||
private Base(int dbType, DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(Set.class, dbType, docPropertyType, nullable);
|
||||
this.keepSource = keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean jsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set read(DataReader reader) throws SQLException {
|
||||
String json = reader.getString();
|
||||
if (keepSource) {
|
||||
reader.pushJson(json);
|
||||
}
|
||||
return readJsonConvert(json);
|
||||
}
|
||||
|
||||
Set readJsonConvert(String json) {
|
||||
try {
|
||||
return EJson.parseSet(json, true);
|
||||
Set<Object> rawSet = EJson.parseSet(json, false);
|
||||
if (rawSet == null) {
|
||||
return null;
|
||||
}
|
||||
final Set result = new LinkedHashSet(rawSet.size());
|
||||
for (Object o : rawSet) {
|
||||
result.add(converter.fromSerialized(o));
|
||||
}
|
||||
return new ModifyAwareSet(result);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as Set", json, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(DataBinder binder, Set value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else {
|
||||
bindRawJson(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
if (nullable) {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
} else {
|
||||
binder.setString("[]");
|
||||
}
|
||||
}
|
||||
|
||||
protected void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String formatValue(Set value) {
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to format List into JSON content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return convertList(EJson.parseList(value));
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to parse JSON content as Set: " + value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set jsonRead(JsonParser parser) throws IOException {
|
||||
return convertList(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void jsonWrite(JsonGenerator writer, Set value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set convertList(List list) {
|
||||
return new LinkedHashSet(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Postgres extension to base List handling.
|
||||
*/
|
||||
private static class PgBase extends ScalarTypeJsonSet.Base {
|
||||
|
||||
final String pgType;
|
||||
|
||||
PgBase(int jdbcType, String pgType, DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(jdbcType, docPropertyType, nullable, keepSource);
|
||||
this.pgType = pgType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, rawJson));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, nullable ? null : "[]"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarTypeBase;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Base for the built-in JSON value types (Map, List, Set).
|
||||
* <p>
|
||||
* Holds the common read / bind / L2 cache plumbing and delegates:
|
||||
* <ul>
|
||||
* <li>the storage concern (VARCHAR / CLOB / BLOB / Postgres) to a {@link JsonStorage}</li>
|
||||
* <li>the value marshalling concern to the subclass (which uses the avaje JsonMapper
|
||||
* backed {@code EJson} facade)</li>
|
||||
* </ul>
|
||||
* This removes the previous explosion of per-storage and per-platform subclasses.
|
||||
*/
|
||||
abstract class ScalarTypeJsonValue<T> extends ScalarTypeBase<T> {
|
||||
|
||||
protected final JsonStorage storage;
|
||||
protected final boolean keepSource;
|
||||
private final boolean nullable;
|
||||
private final String emptyJson;
|
||||
private final DocPropertyType docType;
|
||||
|
||||
/**
|
||||
* @param emptyJson JSON bound when the value is null and the property is not nullable
|
||||
* (e.g. {@code "[]"} for collections), or null to always bind SQL null.
|
||||
*/
|
||||
ScalarTypeJsonValue(Class<T> type, int jdbcType, JsonStorage storage, boolean keepSource,
|
||||
boolean nullable, String emptyJson, DocPropertyType docType) {
|
||||
super(type, false, jdbcType);
|
||||
this.storage = storage;
|
||||
this.keepSource = keepSource;
|
||||
this.nullable = nullable;
|
||||
this.emptyJson = emptyJson;
|
||||
this.docType = docType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the raw JSON read from the DB into the bean value (the load path - typically
|
||||
* returns a modify-aware collection).
|
||||
*/
|
||||
abstract T readJson(String rawJson);
|
||||
|
||||
@Override
|
||||
public final boolean mutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDirty(Object value) {
|
||||
return TypeJsonManager.checkIsDirty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean jsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T read(DataReader reader) throws SQLException {
|
||||
String rawJson = storage.read(reader);
|
||||
if (keepSource) {
|
||||
reader.pushJson(rawJson);
|
||||
}
|
||||
if (rawJson == null) {
|
||||
return null;
|
||||
}
|
||||
return readJson(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(DataBinder binder, T value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
if (nullable || emptyJson == null) {
|
||||
storage.bindNull(binder);
|
||||
} else {
|
||||
storage.bind(binder, emptyJson);
|
||||
}
|
||||
} else {
|
||||
storage.bind(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final T toBeanType(Object value) {
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DocPropertyType docType() {
|
||||
return docType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
}
|
||||
return parse(dataInput.readUTF());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeData(DataOutput dataOutput, T value) throws IOException {
|
||||
if (value == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
ScalarHelp.writeUTF(dataOutput, formatValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonReader.Token;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.config.dbplatform.ExtraDbTypes;
|
||||
import io.ebean.core.type.ScalarTypeBaseDateTime;
|
||||
@@ -48,13 +49,19 @@ final class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTime
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime jsonRead(JsonParser parser) throws IOException {
|
||||
return parse(parser.getText());
|
||||
public LocalDateTime jsonRead(JsonReader parser) throws IOException {
|
||||
if (parser.isNullValue()) {
|
||||
return null;
|
||||
}
|
||||
if (parser.currentToken() == Token.NUMBER) {
|
||||
return parse(parser.readDecimal().toPlainString());
|
||||
}
|
||||
return parse(parser.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, LocalDateTime value) throws IOException {
|
||||
writer.writeString(value.toString());
|
||||
public void jsonWrite(JsonWriter writer, LocalDateTime value) throws IOException {
|
||||
writer.value(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -87,13 +87,13 @@ class ScalarTypeLocalTime extends ScalarTypeBase<LocalTime> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalTime jsonRead(JsonParser parser) throws IOException {
|
||||
return LocalTime.parse(parser.getValueAsString());
|
||||
public LocalTime jsonRead(JsonReader parser) throws IOException {
|
||||
return LocalTime.parse(parser.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, LocalTime value) throws IOException {
|
||||
writer.writeString(value.toString());
|
||||
public void jsonWrite(JsonWriter writer, LocalTime value) throws IOException {
|
||||
writer.value(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -87,13 +87,13 @@ final class ScalarTypeLong extends ScalarTypeBase<Long> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getLongValue();
|
||||
public Long jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Long value) throws IOException {
|
||||
writer.writeNumber(value);
|
||||
public void jsonWrite(JsonWriter writer, Long value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -82,13 +82,13 @@ final class ScalarTypeMathBigInteger extends ScalarTypeBase<BigInteger> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger jsonRead(JsonParser parser) throws IOException {
|
||||
return parser.getDecimalValue().toBigInteger();
|
||||
public BigInteger jsonRead(JsonReader parser) throws IOException {
|
||||
return parser.readBigInteger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, BigInteger value) throws IOException {
|
||||
writer.writeNumber(value.longValue());
|
||||
public void jsonWrite(JsonWriter writer, BigInteger value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
@@ -102,13 +102,13 @@ final class ScalarTypeMonthDay extends ScalarTypeBase<MonthDay> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonthDay jsonRead(JsonParser parser) throws IOException {
|
||||
return parse(parser.getValueAsString());
|
||||
public MonthDay jsonRead(JsonReader parser) throws IOException {
|
||||
return parse(parser.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, MonthDay value) throws IOException {
|
||||
writer.writeString(format(value));
|
||||
public void jsonWrite(JsonWriter writer, MonthDay value) throws IOException {
|
||||
writer.value(format(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,8 +5,8 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.avaje.json.JsonReader;
|
||||
import io.avaje.json.JsonWriter;
|
||||
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
@@ -84,12 +84,12 @@ class ScalarTypeNotFound implements ScalarType<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void jsonRead(JsonParser parser) throws IOException {
|
||||
public Void jsonRead(JsonReader parser) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Void value) throws IOException {
|
||||
public void jsonWrite(JsonWriter writer, Void value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user