Skip to content

Commit abfafb9

Browse files
committed
Use H2 DB and add ability to customize DB driver etc.
1 parent 84e2442 commit abfafb9

5 files changed

Lines changed: 130 additions & 58 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,13 @@
33
[![Build Status](https://travis-ci.org/terma/sql-on-json.svg?branch=master)](https://travis-ci.org/terma/sql-on-json)
44
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.terma/sql-on-json/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.terma/sql-on-json/)
55

6+
## How to use
7+
8+
```java
9+
try (Connection c = sqlOnJson.convertPlain("{a:[{id:12000,name:\"super\"},{id:90,name:\"remta\"}]}")) {
10+
ResultSet rs = c.prepareStatement("select * from a").executeQuery();
11+
while (rs.next()) {
12+
// my business logic
13+
}
14+
}
15+
```

pom.xml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
34
<modelVersion>4.0.0</modelVersion>
45

56
<groupId>com.github.terma</groupId>
@@ -95,9 +96,9 @@
9596

9697
<dependencies>
9798
<dependency>
98-
<groupId>org.hsqldb</groupId>
99-
<artifactId>hsqldb</artifactId>
100-
<version>2.3.5</version>
99+
<groupId>com.h2database</groupId>
100+
<artifactId>h2</artifactId>
101+
<version>1.4.195</version>
101102
</dependency>
102103

103104
<dependency>
@@ -112,6 +113,13 @@
112113
<version>3.5</version>
113114
</dependency>
114115

116+
<dependency>
117+
<groupId>org.hsqldb</groupId>
118+
<artifactId>hsqldb</artifactId>
119+
<version>2.3.5</version>
120+
<scope>test</scope>
121+
</dependency>
122+
115123
<dependency>
116124
<groupId>junit</groupId>
117125
<artifactId>junit</artifactId>

src/main/java/com/github/terma/sqlonjson/SqlOnJson.java

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
/**
3535
* Convert JSON to in memory SQL database with tables created from JSON.
3636
* <p>
37-
* Create in memory DB {@link org.hsqldb.jdbc.JDBCDriver} which will be destroyed as soon as
37+
* Create in memory DB which will be destroyed as soon as
3838
* connection will be closed.
3939
* <p>
4040
* Thread safe. Each call will create new independent SQL DB.
@@ -44,10 +44,69 @@
4444
@SuppressWarnings("WeakerAccess")
4545
public class SqlOnJson {
4646

47-
private static final AtomicInteger COUNTER = new AtomicInteger();
48-
private static final String DRIVER_CLASS = "org.hsqldb.jdbc.JDBCDriver";
47+
public static final String INSTANCE_ID_PLACEHOLDER = "<INSTANCE_ID>";
48+
49+
public static final String DEFAULT_DRIVER = "org.h2.Driver";
50+
public static final String DEFAULT_URL = "jdbc:h2:mem:";
51+
public static final String DEFAULT_USERNAME = "";
52+
public static final String DEFAULT_PASSWORD = "";
53+
4954
private static final Logger LOGGER = Logger.getLogger(SqlOnJson.class.getName());
5055

56+
private final AtomicInteger counter;
57+
private final String driver;
58+
private final String url;
59+
private final String username;
60+
private final String password;
61+
62+
/**
63+
* @param driver DB driver class which implement JDBC interface
64+
* @param url regular JDBC URL string with optional {@link SqlOnJson#INSTANCE_ID_PLACEHOLDER} placeholder which will be
65+
* replaced on instance ID during conversion to make sure that all instances are unique
66+
*/
67+
public SqlOnJson(String driver, String url, String username, String password) {
68+
this.counter = new AtomicInteger();
69+
this.driver = driver;
70+
this.url = url;
71+
this.username = username;
72+
this.password = password;
73+
}
74+
75+
/**
76+
* With default DB H2 in in-memory private mode (DB life until connection will be closed)
77+
*/
78+
public SqlOnJson() {
79+
this(DEFAULT_DRIVER, DEFAULT_URL, DEFAULT_USERNAME, DEFAULT_PASSWORD);
80+
}
81+
82+
private static LinkedHashMap<String, ColumnType> getColumns(JsonTable jsonTable) {
83+
final LinkedHashMap<String, ColumnType> cls = new LinkedHashMap<>();
84+
85+
for (int i = 0; i < jsonTable.data.size(); i++) {
86+
final JsonObject jsonObject = jsonTable.data.get(i).getAsJsonObject();
87+
for (Map.Entry<String, JsonElement> part : jsonObject.entrySet()) {
88+
ColumnType columnType = ColumnType.STRING;
89+
if (part.getValue().isJsonPrimitive()) {
90+
if (part.getValue().getAsString().matches("[-0-9]+")) {
91+
columnType = ColumnType.BIGINT;
92+
} else if (part.getValue().getAsString().matches("[-0-9.]+")) {
93+
columnType = ColumnType.DOUBLE;
94+
}
95+
}
96+
97+
cls.put(part.getKey(), columnType);
98+
}
99+
}
100+
return cls;
101+
}
102+
103+
private static String nameToSqlName(final String columnName) {
104+
String first = columnName.substring(0, 1);
105+
if (first.matches("[^a-zA-Z]")) first = "i";
106+
107+
return first + columnName.substring(1).replaceAll("[^a-zA-Z0-9_]+", "");
108+
}
109+
51110
/**
52111
* Convert JSON to SQL and assume that root of JSON is Object properties
53112
* for which with array type could be converted to tables {@link Plain}
@@ -57,17 +116,17 @@ public class SqlOnJson {
57116
* @throws SQLException
58117
* @throws ClassNotFoundException
59118
*/
60-
public static Connection convertPlain(String json) throws SQLException, ClassNotFoundException {
119+
public Connection convertPlain(String json) throws SQLException, ClassNotFoundException {
61120
return convert(new Plain(json));
62121
}
63122

64-
public static Connection convert(JsonIterator jsonIterator) throws SQLException, ClassNotFoundException {
65-
Class.forName(DRIVER_CLASS);
123+
public Connection convert(JsonIterator jsonIterator) throws SQLException, ClassNotFoundException {
124+
Class.forName(driver);
66125

67-
if (COUNTER.get() > Integer.MAX_VALUE - 10) COUNTER.set(0); // to avoid possible overflow, who knows =)
68-
final int id = COUNTER.incrementAndGet();
126+
if (counter.get() > Integer.MAX_VALUE - 10) counter.set(0); // to avoid possible overflow, who knows =)
127+
final int id = counter.incrementAndGet();
69128

70-
final Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:sql_on_json_" + id + ";shutdown=true", "SA", "");
129+
final Connection c = DriverManager.getConnection(url.replaceAll(INSTANCE_ID_PLACEHOLDER, String.valueOf(id)), username, password);
71130
try {
72131
final long start = System.currentTimeMillis();
73132

@@ -120,32 +179,4 @@ public static Connection convert(JsonIterator jsonIterator) throws SQLException,
120179
return c;
121180
}
122181

123-
private static LinkedHashMap<String, ColumnType> getColumns(JsonTable jsonTable) {
124-
final LinkedHashMap<String, ColumnType> cls = new LinkedHashMap<>();
125-
126-
for (int i = 0; i < jsonTable.data.size(); i++) {
127-
final JsonObject jsonObject = jsonTable.data.get(i).getAsJsonObject();
128-
for (Map.Entry<String, JsonElement> part : jsonObject.entrySet()) {
129-
ColumnType columnType = ColumnType.STRING;
130-
if (part.getValue().isJsonPrimitive()) {
131-
if (part.getValue().getAsString().matches("[-0-9]+")) {
132-
columnType = ColumnType.BIGINT;
133-
} else if (part.getValue().getAsString().matches("[-0-9.]+")) {
134-
columnType = ColumnType.DOUBLE;
135-
}
136-
}
137-
138-
cls.put(part.getKey(), columnType);
139-
}
140-
}
141-
return cls;
142-
}
143-
144-
private static String nameToSqlName(final String columnName) {
145-
String first = columnName.substring(0, 1);
146-
if (first.matches("[^a-zA-Z]")) first = "i";
147-
148-
return first + columnName.substring(1).replaceAll("[^a-zA-Z0-9_]+", "");
149-
}
150-
151182
}

src/test/java/com/github/terma/sqlonjson/SqlOnJsonPerf.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ private static StringBuilder generateJson() {
4949
public void test() throws SQLException, ClassNotFoundException {
5050
String json = generateJson().toString();
5151

52-
try (Connection c = SqlOnJson.convertPlain(json)) {
53-
try (PreparedStatement ps = c.prepareStatement("select * as count_of_rows from sources order by id desc limit 5")) {
52+
try (Connection c = new SqlOnJson().convertPlain(json)) {
53+
try (PreparedStatement ps = c.prepareStatement("select as count_of_rows from sources order by id desc limit 5")) {
5454
try (ResultSet rs = ps.executeQuery()) {
5555
printResultSet(rs);
5656
}

src/test/java/com/github/terma/sqlonjson/SqlOnJsonTest.java

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,27 @@
2626
@SuppressWarnings("SqlNoDataSourceInspection")
2727
public class SqlOnJsonTest {
2828

29+
private final SqlOnJson sqlOnJson = new SqlOnJson();
30+
2931
@Test
3032
public void representEmptyJsonAsEmptyDb() throws Exception {
31-
try (Connection c = SqlOnJson.convertPlain("")) {
33+
try (Connection c = sqlOnJson.convertPlain("")) {
3234
ResultSet rs = c.getMetaData().getTables(null, null, "a", null);
3335
Assert.assertFalse(rs.next());
3436
}
3537
}
3638

3739
@Test
3840
public void representObjectWithEmptyArrayPropertyAsEmptyDb() throws Exception {
39-
try (Connection c = SqlOnJson.convertPlain("{a:[]}")) {
41+
try (Connection c = sqlOnJson.convertPlain("{a:[]}")) {
4042
ResultSet rs = c.getMetaData().getTables(null, null, "a", null);
4143
Assert.assertFalse(rs.next());
4244
}
4345
}
4446

4547
@Test
4648
public void representObjectWithArrayPropertyAsTableInDb() throws Exception {
47-
try (Connection c = SqlOnJson.convertPlain("{a:[{id:12000,name:\"super\"},{id:90,name:\"remta\"}]}")) {
49+
try (Connection c = sqlOnJson.convertPlain("{a:[{id:12000,name:\"super\"},{id:90,name:\"remta\"}]}")) {
4850
ResultSet rs = c.prepareStatement("select * from a").executeQuery();
4951
rs.next();
5052
Assert.assertEquals(12000, rs.getLong("id"));
@@ -57,7 +59,7 @@ public void representObjectWithArrayPropertyAsTableInDb() throws Exception {
5759

5860
@Test
5961
public void ignoreNonAlphaNumberAndNonAlphaFirstCharacters() throws Exception {
60-
try (Connection c = SqlOnJson.convertPlain("{\"_AmO_(Nit)\":[{\"_i-d,()rumbA\":12000,_12:90}]}")) {
62+
try (Connection c = sqlOnJson.convertPlain("{\"_AmO_(Nit)\":[{\"_i-d,()rumbA\":12000,_12:90}]}")) {
6163
ResultSet rs = c.prepareStatement("select * from iamo_nit").executeQuery();
6264
rs.next();
6365
Assert.assertEquals(12000, rs.getLong("iidrumba"));
@@ -68,7 +70,7 @@ public void ignoreNonAlphaNumberAndNonAlphaFirstCharacters() throws Exception {
6870
@Test
6971
public void support8kOfCharactersForStringFields() throws Exception {
7072
String string8k = StringUtils.repeat('z', 8 * 1000);
71-
try (Connection c = SqlOnJson.convertPlain("{longs:[{str:\"" + string8k + "\"}]}")) {
73+
try (Connection c = sqlOnJson.convertPlain("{longs:[{str:\"" + string8k + "\"}]}")) {
7274
ResultSet rs = c.prepareStatement("select * from longs").executeQuery();
7375
rs.next();
7476
Assert.assertEquals(string8k, rs.getString("str"));
@@ -77,7 +79,7 @@ public void support8kOfCharactersForStringFields() throws Exception {
7779

7880
@Test
7981
public void supportCaseWhenNonFirstObjectHasMoreProperties() throws Exception {
80-
try (Connection c = SqlOnJson.convertPlain("{nosql:[{id:12},{id:15,mid:90}]}")) {
82+
try (Connection c = sqlOnJson.convertPlain("{nosql:[{id:12},{id:15,mid:90}]}")) {
8183
ResultSet rs = c.prepareStatement("select * from nosql").executeQuery();
8284
rs.next();
8385
Assert.assertEquals(12, rs.getLong("id"));
@@ -90,8 +92,8 @@ public void supportCaseWhenNonFirstObjectHasMoreProperties() throws Exception {
9092
@Test
9193
public void supportParallelWorkWithTwoDb() throws Exception {
9294
try (
93-
Connection c1 = SqlOnJson.convertPlain("{nosql:[{id:12},{id:15,mid:90}]}");
94-
Connection c2 = SqlOnJson.convertPlain("{nosql:[{a:1}]}");
95+
Connection c1 = sqlOnJson.convertPlain("{nosql:[{id:12},{id:15,mid:90}]}");
96+
Connection c2 = sqlOnJson.convertPlain("{nosql:[{a:1}]}");
9597
) {
9698
ResultSet rs1 = c1.prepareStatement("select * from nosql").executeQuery();
9799
Assert.assertTrue(rs1.next());
@@ -102,7 +104,7 @@ public void supportParallelWorkWithTwoDb() throws Exception {
102104

103105
@Test
104106
public void representNumberPropertyAsLong() throws Exception {
105-
try (Connection c = SqlOnJson.convertPlain("{a:[{o:" + Long.MAX_VALUE + "},{o:" + Long.MIN_VALUE + "}]}")) {
107+
try (Connection c = sqlOnJson.convertPlain("{a:[{o:" + Long.MAX_VALUE + "},{o:" + Long.MIN_VALUE + "}]}")) {
106108
ResultSet rs = c.prepareStatement("select * from a").executeQuery();
107109
Assert.assertEquals("BIGINT", rs.getMetaData().getColumnTypeName(1));
108110
rs.next();
@@ -114,7 +116,7 @@ public void representNumberPropertyAsLong() throws Exception {
114116

115117
@Test
116118
public void representNumberWithPrecisionPropertyAsDouble() throws Exception {
117-
try (Connection c = SqlOnJson.convertPlain("{a:[{o:0.009},{o:-12.45}]}")) {
119+
try (Connection c = sqlOnJson.convertPlain("{a:[{o:0.009},{o:-12.45}]}")) {
118120
ResultSet rs = c.prepareStatement("select * from a").executeQuery();
119121
Assert.assertEquals("DOUBLE", rs.getMetaData().getColumnTypeName(1));
120122
rs.next();
@@ -126,7 +128,7 @@ public void representNumberWithPrecisionPropertyAsDouble() throws Exception {
126128

127129
@Test
128130
public void representObjectWithArrayPropertyWithMissedAttributesAsTableInDbWithNull() throws Exception {
129-
try (Connection c = SqlOnJson.convertPlain("{a:[{id:12000,name:\"super\"},{id:90}]}")) {
131+
try (Connection c = sqlOnJson.convertPlain("{a:[{id:12000,name:\"super\"},{id:90}]}")) {
130132
ResultSet rs = c.prepareStatement("select * from a").executeQuery();
131133
rs.next();
132134
Assert.assertEquals(12000, rs.getLong("id"));
@@ -139,7 +141,7 @@ public void representObjectWithArrayPropertyWithMissedAttributesAsTableInDbWithN
139141

140142
@Test
141143
public void representObjectWithArrayPropertiesAsMultipleTables() throws Exception {
142-
try (Connection c = SqlOnJson.convertPlain("{orders:[{id:12}],history:[{orderId:12}]}")) {
144+
try (Connection c = sqlOnJson.convertPlain("{orders:[{id:12}],history:[{orderId:12}]}")) {
143145
ResultSet rs1 = c.prepareStatement("select * from orders").executeQuery();
144146
rs1.next();
145147
Assert.assertEquals(12, rs1.getLong("id"));
@@ -152,7 +154,7 @@ public void representObjectWithArrayPropertiesAsMultipleTables() throws Exceptio
152154

153155
@Test
154156
public void representEmbeddedObjectAsString() throws Exception {
155-
try (Connection c = SqlOnJson.convertPlain("{orders:[{em:{a:12}}]}")) {
157+
try (Connection c = sqlOnJson.convertPlain("{orders:[{em:{a:12}}]}")) {
156158
ResultSet rs1 = c.prepareStatement("select * from orders").executeQuery();
157159
rs1.next();
158160
Assert.assertEquals("{\"a\":12}", rs1.getString("em"));
@@ -162,7 +164,7 @@ public void representEmbeddedObjectAsString() throws Exception {
162164

163165
@Test
164166
public void supportEmbeddedArrayToTable() throws Exception {
165-
try (Connection c = SqlOnJson.convertPlain("{orders:[{em:[{a:-7}]}]}")) {
167+
try (Connection c = sqlOnJson.convertPlain("{orders:[{em:[{a:-7}]}]}")) {
166168
ResultSet rs1 = c.prepareStatement("select * from orders").executeQuery();
167169
rs1.next();
168170
Assert.assertEquals("[{\"a\":-7}]", rs1.getString("em"));
@@ -172,10 +174,31 @@ public void supportEmbeddedArrayToTable() throws Exception {
172174

173175
@Test
174176
public void supportEmbeddedObjectAsTable() throws Exception {
175-
try (Connection c = SqlOnJson.convertPlain("{orders:{em:[{a:-7}]}}")) {
177+
try (Connection c = sqlOnJson.convertPlain("{orders:{em:[{a:-7}]}}")) {
176178
ResultSet rs1 = c.getMetaData().getTables(null, null, "orders", null);
177179
Assert.assertFalse(rs1.next());
178180
}
179181
}
180182

183+
@Test
184+
public void supportRenamingOfColumnsForDefaultDb() throws Exception {
185+
try (Connection c = sqlOnJson.convertPlain("{orders:[{user_id:12,id:900}],users:[{id:12}]}")) {
186+
ResultSet rs1 = c.prepareStatement("select o.id as oid, u.id as uid from orders o left join users u on user_id = u.id").executeQuery();
187+
rs1.next();
188+
Assert.assertEquals("12", rs1.getString("uid"));
189+
Assert.assertEquals("900", rs1.getString("oid"));
190+
}
191+
}
192+
193+
@Test
194+
public void supportCustomDb() throws Exception {
195+
try (Connection c = new SqlOnJson("org.hsqldb.jdbc.JDBCDriver", "jdbc:hsqldb:mem:sql_on_json_test;shutdown=true", "sa", "")
196+
.convertPlain("{orders:[{user_id:13,id:900}],users:[{id:13}]}")) {
197+
ResultSet rs1 = c.prepareStatement("select o.id as oid, u.id as uid from orders o left join users u on user_id = u.id").executeQuery();
198+
rs1.next();
199+
Assert.assertEquals("13", rs1.getString("uid"));
200+
Assert.assertEquals("900", rs1.getString("oid"));
201+
}
202+
}
203+
181204
}

0 commit comments

Comments
 (0)