[New] Implemented PostgreSQL querying

This commit is contained in:
Robert von Burg 2014-08-25 22:53:16 +02:00
parent 0e75f9b0d2
commit 65402e9006
6 changed files with 684 additions and 6 deletions

View File

@ -19,9 +19,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
@ -46,6 +48,8 @@ import org.xml.sax.SAXException;
@SuppressWarnings("nls")
public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao {
public static final String ORDERS = "orders";
/**
* @param tx
*/
@ -60,7 +64,7 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
@Override
protected String getTableName() {
return "orders";
return ORDERS;
}
@Override
@ -157,7 +161,29 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
@Override
public <U> List<U> doQuery(OrderQuery query, OrderVisitor<U> orderVisitor) {
// TODO Auto-generated method stub
return null;
PostgreSqlOrderQueryVisitor queryVisitor = new PostgreSqlOrderQueryVisitor("id, asxml");
query.accept(queryVisitor);
queryVisitor.validate();
List<U> list = new ArrayList<>();
String sql = queryVisitor.getSql();
try (PreparedStatement ps = PostgreSqlOrderDao.this.tx.getConnection().prepareStatement(sql)) {
queryVisitor.setValues(ps);
try (ResultSet result = ps.executeQuery()) {
while (result.next()) {
String id = result.getString("id");
SQLXML sqlxml = result.getSQLXML("asxml");
Order t = parseFromXml(id, queryVisitor.getType(), sqlxml);
list.add(orderVisitor.visit(t));
}
}
} catch (SQLException e) {
throw new StrolchPersistenceException("Failed to perform query due to: " + e.getMessage(), e);
}
return list;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql;
import li.strolch.model.Tags;
import li.strolch.model.query.DateSelection;
import li.strolch.model.query.OrderQueryVisitor;
import li.strolch.model.query.StateSelection;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PostgreSqlOrderQueryVisitor extends PostgreSqlQueryVisitor implements OrderQueryVisitor {
/**
* @param fields
*/
public PostgreSqlOrderQueryVisitor(String fields) {
super(fields);
}
protected String getClassName() {
return Tags.ORDER;
}
protected String getTableName() {
return PostgreSqlOrderDao.ORDERS;
}
@Override
public void visit(DateSelection selection) {
sb.append(indent);
sb.append("date = ?\n");
values.add(selection.getDate());
}
@Override
public void visit(StateSelection selection) {
sb.append(indent);
sb.append("stae = ?\n");
values.add(selection.getState().name());
}
}

View File

@ -0,0 +1,307 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import li.strolch.model.query.AndSelection;
import li.strolch.model.query.IdSelection;
import li.strolch.model.query.NameSelection;
import li.strolch.model.query.NotSelection;
import li.strolch.model.query.OrSelection;
import li.strolch.model.query.ParameterSelection.BooleanParameterSelection;
import li.strolch.model.query.ParameterSelection.DateParameterSelection;
import li.strolch.model.query.ParameterSelection.DateRangeParameterSelection;
import li.strolch.model.query.ParameterSelection.FloatParameterSelection;
import li.strolch.model.query.ParameterSelection.IntegerParameterSelection;
import li.strolch.model.query.ParameterSelection.LongParameterSelection;
import li.strolch.model.query.ParameterSelection.StringListParameterSelection;
import li.strolch.model.query.ParameterSelection.StringParameterSelection;
import li.strolch.model.query.ParameterSelectionVisitor;
import li.strolch.model.query.Selection;
import li.strolch.model.query.StrolchElementSelectionVisitor;
import li.strolch.model.query.StrolchTypeNavigation;
import ch.eitchnet.utils.StringMatchMode;
import ch.eitchnet.utils.dbc.DBC;
import ch.eitchnet.utils.iso8601.ISO8601FormatFactory;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public abstract class PostgreSqlQueryVisitor implements StrolchElementSelectionVisitor, ParameterSelectionVisitor {
protected StringBuilder sql;
protected StringBuilder sb;
protected String type;
protected List<Object> values;
protected boolean any;
protected String indent;
private String sqlAsString;
public PostgreSqlQueryVisitor(String fields) {
this.indent = "";
this.sql = new StringBuilder();
this.sb = new StringBuilder();
this.values = new ArrayList<>();
this.sql.append("select ");
this.sql.append(fields);
this.sql.append("\nfrom\n");
this.sql.append(" ");
this.sql.append(getTableName());
this.indent = " ";
}
public String getSql() {
if (sqlAsString != null)
return sqlAsString;
this.sql.append("\nwhere\n");
this.sql.append(this.indent);
if (this.any) {
this.sql.append("type = ?");
sqlAsString = this.sql.toString();
return sqlAsString;
}
this.sql.append("type = ? and\n");
this.sql.append(this.sb.toString());
sqlAsString = this.sql.toString();
return sqlAsString;
}
/**
* @return the any
*/
public boolean isAny() {
return this.any;
}
/**
* @return the values
*/
public List<Object> getValues() {
return this.values;
}
public String getType() {
return this.type;
}
public void validate() {
DBC.INTERIM.assertNotEmpty("No navigation was set!", this.type);
}
protected abstract String getClassName();
protected abstract String getTableName();
@Override
public void visit(StrolchTypeNavigation navigation) {
this.type = navigation.getType();
}
@Override
public void visit(IdSelection selection) {
this.sb.append(this.indent);
List<String> ids = selection.getIds();
if (ids.isEmpty())
return;
int size = ids.size();
if (size == 1) {
this.sb.append("id = ?\n");
this.values.add(ids.get(0));
} else {
this.sb.append("id in (");
Iterator<String> iter = ids.iterator();
while (iter.hasNext()) {
String id = iter.next();
this.sb.append("?");
this.values.add(id);
if (iter.hasNext())
this.sb.append(", ");
}
this.sb.append(" )\n");
}
}
@Override
public void visit(NameSelection selection) {
this.sb.append(this.indent);
String name = selection.getName();
// CS EQ
// 1. x x
// 2. x o
// 3. o x
// 4. o o
StringMatchMode mm = selection.getMatchMode();
if (mm.isCaseSensitve() && mm.isEquals()) {
this.sb.append("name = ?\n");
this.values.add(name);
} else if (!mm.isCaseSensitve() && mm.isEquals()) {
this.sb.append("lower(name) = lower(?)\n");
this.values.add(name);
} else if (!mm.isEquals() && mm.isCaseSensitve()) {
this.sb.append("name like ?");
this.values.add("%" + name + "%");
} else {
this.sb.append("lower(name) like ?");
this.values.add(name.toLowerCase());
}
}
@Override
public void visitAny() {
this.any = true;
}
@Override
public void visitAnd(AndSelection andSelection) {
this.sb.append(this.indent);
List<Selection> selections = andSelection.getSelections();
this.sb.append("( \n");
Iterator<Selection> iter = selections.iterator();
String indent = this.indent;
this.indent += " ";
while (iter.hasNext()) {
Selection selection = iter.next();
selection.accept(this);
if (iter.hasNext()) {
this.sb.append(indent);
this.sb.append("and\n");
}
}
this.indent = indent;
this.sb.append(this.indent);
this.sb.append(")\n");
}
@Override
public void visitOr(OrSelection orSelection) {
this.sb.append(this.indent);
List<Selection> selections = orSelection.getSelections();
this.sb.append("( \n");
Iterator<Selection> iter = selections.iterator();
String indent = this.indent;
this.indent += " ";
while (iter.hasNext()) {
Selection selection = iter.next();
selection.accept(this);
if (iter.hasNext()) {
this.sb.append(indent);
this.sb.append("or\n");
}
}
this.indent = indent;
this.sb.append(this.indent);
this.sb.append(")\n");
}
@Override
public void visitNot(NotSelection notSelection) {
this.sb.append(this.indent);
List<Selection> selections = notSelection.getSelections();
this.sb.append("not ( \n");
Iterator<Selection> iter = selections.iterator();
String indent = this.indent;
this.indent += " ";
while (iter.hasNext()) {
Selection selection = iter.next();
selection.accept(this);
if (iter.hasNext()) {
this.sb.append(indent);
this.sb.append("and\n");
}
}
this.indent = indent;
this.sb.append(this.indent);
this.sb.append(")\n");
}
private void xpath(String bagKey, String paramKey, String paramValue) {
this.sb.append(this.indent);
String xpath = "cast(xpath('//Resource/ParameterBag[@Id=\"${bagKey}\"]/Parameter[@Id=\"${paramKey}\" and @Value=\"${paramValue}\"]', asxml) as text[]) != '{}'\n";
xpath = xpath.replace("${bagKey}", bagKey);
xpath = xpath.replace("${paramKey}", paramKey);
xpath = xpath.replace("${paramValue}", paramValue);
this.sb.append(xpath);
}
@Override
public void visit(StringParameterSelection selection) {
xpath(selection.getBagKey(), selection.getParamKey(), selection.getValue());
}
@Override
public void visit(IntegerParameterSelection selection) {
xpath(selection.getBagKey(), selection.getParamKey(), selection.getValue().toString());
}
@Override
public void visit(BooleanParameterSelection selection) {
xpath(selection.getBagKey(), selection.getParamKey(), selection.getValue().toString());
}
@Override
public void visit(LongParameterSelection selection) {
xpath(selection.getBagKey(), selection.getParamKey(), selection.getValue().toString());
}
@Override
public void visit(FloatParameterSelection selection) {
xpath(selection.getBagKey(), selection.getParamKey(), selection.getValue().toString());
}
@Override
public void visit(DateParameterSelection selection) {
xpath(selection.getBagKey(), selection.getParamKey(),
ISO8601FormatFactory.getInstance().formatDate(selection.getValue()));
}
@Override
public void visit(DateRangeParameterSelection selection) {
throw new UnsupportedOperationException("Not yet supported!");
}
@Override
public void visit(StringListParameterSelection selection) {
throw new UnsupportedOperationException("Not yet supported!");
}
/**
* @param ps
* @throws SQLException
*/
public void setValues(PreparedStatement ps) throws SQLException {
if (this.any) {
ps.setString(1, this.type);
return;
}
ps.setString(1, this.type);
for (int i = 0; i < this.values.size(); i++) {
ps.setObject(i + 2, this.values.get(i));
}
}
}

View File

@ -18,9 +18,11 @@ package li.strolch.persistence.postgresql;
import java.io.IOException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
@ -44,6 +46,8 @@ import org.xml.sax.SAXException;
@SuppressWarnings("nls")
public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements ResourceDao {
public static final String RESOURCES = "resources";
protected PostgreSqlResourceDao(PostgreSqlStrolchTransaction tx) {
super(tx);
}
@ -55,7 +59,7 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
@Override
protected String getTableName() {
return "resources";
return RESOURCES;
}
@Override
@ -146,7 +150,29 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
@Override
public <U> List<U> doQuery(ResourceQuery query, ResourceVisitor<U> resourceVisitor) {
// TODO Auto-generated method stub
return null;
PostgreSqlResourceQueryVisitor queryVisitor = new PostgreSqlResourceQueryVisitor("id, asxml");
query.accept(queryVisitor);
queryVisitor.validate();
List<U> list = new ArrayList<>();
String sql = queryVisitor.getSql();
try (PreparedStatement ps = PostgreSqlResourceDao.this.tx.getConnection().prepareStatement(sql)) {
queryVisitor.setValues(ps);
try (ResultSet result = ps.executeQuery()) {
while (result.next()) {
String id = result.getString("id");
SQLXML sqlxml = result.getSQLXML("asxml");
Resource t = parseFromXml(id, queryVisitor.getType(), sqlxml);
list.add(resourceVisitor.visit(t));
}
}
} catch (SQLException e) {
throw new StrolchPersistenceException("Failed to perform query due to: " + e.getMessage(), e);
}
return list;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql;
import li.strolch.model.Tags;
import li.strolch.model.query.ResourceQueryVisitor;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PostgreSqlResourceQueryVisitor extends PostgreSqlQueryVisitor implements ResourceQueryVisitor {
/**
* @param fields
*/
public PostgreSqlResourceQueryVisitor(String fields) {
super(fields);
}
protected String getClassName() {
return Tags.RESOURCE;
}
protected String getTableName() {
return PostgreSqlResourceDao.RESOURCES;
}
}

View File

@ -0,0 +1,223 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.persistence.postgresql.dao.test;
import static org.junit.Assert.assertEquals;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import li.strolch.model.query.IdSelection;
import li.strolch.model.query.NameSelection;
import li.strolch.model.query.OrSelection;
import li.strolch.model.query.OrderQuery;
import li.strolch.model.query.ParameterSelection;
import li.strolch.model.query.ResourceQuery;
import li.strolch.model.query.StrolchTypeNavigation;
import li.strolch.persistence.postgresql.PostgreSqlOrderQueryVisitor;
import li.strolch.persistence.postgresql.PostgreSqlQueryVisitor;
import li.strolch.persistence.postgresql.PostgreSqlResourceQueryVisitor;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.StringMatchMode;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class QueryTest {
private static final Logger logger = LoggerFactory.getLogger(QueryTest.class);
public Connection openConn() throws SQLException {
String url = "jdbc:postgresql://localhost/testdb";
String username = "testuser";
String password = "test";
Connection connection = DriverManager.getConnection(url, username, password);
connection.setAutoCommit(false);
return connection;
}
@Test
public void shouldQueryOrderAll() throws SQLException {
OrderQuery query = new OrderQuery(new StrolchTypeNavigation("QTestType1"));
query.withAny();
performOrderQuery(query, Arrays.asList("myTestOrder1"));
}
@Test
public void shouldQueryResourceAll() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.withAny();
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004"));
}
@Test
public void shouldQueryOrder1() throws SQLException {
OrderQuery query = new OrderQuery(new StrolchTypeNavigation("QTestType1"));
query.and().with(new IdSelection("myTestOrder1", "@2"),
new NameSelection("Test Name", StringMatchMode.EQUALS_CASE_SENSITIVE));
performOrderQuery(query, Arrays.asList("myTestOrder1"));
}
@Test
public void shouldQueryOrder2() throws SQLException {
OrderQuery query = new OrderQuery(new StrolchTypeNavigation("QTestType1"));
query.or().with(new IdSelection("myTestOrder1", "@2"),
new NameSelection("Test Name", StringMatchMode.EQUALS_CASE_SENSITIVE));
performOrderQuery(query, Arrays.asList("myTestOrder1"));
}
@Test
public void shouldQueryResource1() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.or().with(new IdSelection("@_00000000", "@_00000001"),
new NameSelection("Test Name", StringMatchMode.EQUALS_CASE_SENSITIVE));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001"));
}
@Test
public void shouldQueryResource2() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(
new OrSelection(new IdSelection("@_00000000"), new IdSelection("@_00000001")),
new OrSelection(new NameSelection("My Resource 0", StringMatchMode.EQUALS_CASE_SENSITIVE),
new NameSelection("My Resource 1", StringMatchMode.EQUALS_CASE_SENSITIVE)));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001"));
}
@Test
public void shouldQueryResourceByBooleParam() throws SQLException {
// select id, name, type, asxml
// from
// resources
// where
// type = 'MyType1' and
// (
// cast(xpath('//Resource/ParameterBag/Parameter[@Id="@param1" and @Value="true"]', asxml) as text[]) != '{}'
// )
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(ParameterSelection.booleanSelection("@bag01", "@param1", true));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004"));
}
@Test
public void shouldQueryResourceByFloagParam() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(ParameterSelection.floatSelection("@bag01", "@param2", 44.3));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004"));
}
@Test
public void shouldQueryResourceByIntegerParam() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(ParameterSelection.integerSelection("@bag01", "@param3", 77));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004"));
}
@Test
public void shouldQueryResourceByLongParam() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(ParameterSelection.longSelection("@bag01", "@param4", 4453234566L));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004"));
}
@Test
public void shouldQueryResourceByStringParam() throws SQLException {
List<String> expected = Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004");
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(
ParameterSelection.stringSelection("@bag01", "@param5", "Strolch",
StringMatchMode.EQUALS_CASE_SENSITIVE));
performResourceQuery(query, expected);
query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(
ParameterSelection.stringSelection("@bag01", "@param5", "strolch",
StringMatchMode.EQUALS_CASE_SENSITIVE));
performResourceQuery(query, Arrays.<String> asList());
query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(
ParameterSelection.stringSelection("@bag01", "@param5", "strolch",
StringMatchMode.EQUALS_CASE_INSENSITIVE));
performResourceQuery(query, expected);
query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(
ParameterSelection.stringSelection("@bag01", "@param5", "olch",
StringMatchMode.CONTAINS_CASE_INSENSITIVE));
performResourceQuery(query, expected);
}
@Test
public void shouldQueryResourceByDateParam() throws SQLException {
ResourceQuery query = new ResourceQuery(new StrolchTypeNavigation("MyType1"));
query.and().with(ParameterSelection.dateSelection("@bag01", "@param6", new Date(1354295525628L)));
performResourceQuery(query, Arrays.asList("@_00000000", "@_00000001", "@_00000002", "@_00000003", "@_00000004"));
}
private void performOrderQuery(OrderQuery query, List<String> expected) throws SQLException {
PostgreSqlOrderQueryVisitor visitor = new PostgreSqlOrderQueryVisitor("id");
query.accept(visitor);
List<String> ids = queryIds(visitor);
assertEquals(expected, ids);
}
private void performResourceQuery(ResourceQuery query, List<String> expected) throws SQLException {
PostgreSqlResourceQueryVisitor visitor = new PostgreSqlResourceQueryVisitor("id");
query.accept(visitor);
List<String> ids = queryIds(visitor);
assertEquals(expected, ids);
}
private List<String> queryIds(PostgreSqlQueryVisitor visitor) throws SQLException {
String sql = visitor.getSql();
logger.info("\n" + sql);
List<String> ids = new ArrayList<>();
try (Connection con = openConn()) {
try (PreparedStatement ps = con.prepareStatement(sql)) {
visitor.setValues(ps);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
ids.add(rs.getString(1));
}
}
}
return ids;
}
}