I'm trying to load a set of objects via a SQL call. The SQL query returns more properties than needed. Other than correcting the SQL query. How would I get groovy to ignore all of the extraneous Parameters upon declaration of Product?
import groovy.sql.Sql
class Product {
Integer id;
Integer type;
String unique;
}
def var = [];
sql = Sql.newInstance("jdbc:jtds:sqlserver://localhost/[DB]", "user", "password","net.sourceforge.jtds.jdbc.Driver");
sql.eachRow("select * from Products", { var << new Product(it.toRowResult()) } );
I'm getting the exception:
groovy.lang.MissingPropertyException: No such property: [other fields from the SQL result]
The default Groovy behavior is to throw a groovy.lang.MissingPropertyException whenever you try to set a value to a property that doesn't exist in the bean. I am not quite sure if you can change that behavior. However, you could write a helper method that filters out the properties that do not exist.
def filterResult(bean, row) {
def filteredProps = [:]
row.each { key, value ->
if(bean.metaClass.properties.find { prop -> prop.name == key }) {
filteredProps.put(key, value)
}
}
filteredProps
}
def var = []
sql.eachRow("select * from Products", {
var << new Product(filterResult(Product, it.toRowResult()))
})
Related
I am writing unit tests with Spock for a series of nested objects. The code I'm writing tests for is quite legacy and doesn't use dependency injection. However it is also quite mission-critical so I'd rather not touch it unless I really need to.
Here is the constructor of the class I'm trying to test:
public SqlTable(Connection conn, String query) throws Exception {
this.statement = conn.createStatement();
this.resultSet = statement.executeQuery(query);
meta = resultSet.getMetaData();
int n = meta.getColumnCount();
columns = new Column[n];
for (int c = 0; c < n; c++) {
columns[c] = new Column(meta.getColumnName(c+1));
// ...
}
}
In the tests I stub the nested mocks in a .groovy file, like this:
def "initialising a SQL table"() {
given:
def COL_NAME = "someColumnName"
def mockResSetMeta = Mock(ResultSetMetaData) {
getColumnCount() >> 1
getColumnName(_ as int) >> COL_NAME
}
and:
def mockResSet = Mock(ResultSet) {
getMetaData() >> mockResSetMeta
}
and:
def mockStatement = Mock(Statement) {
executeQuery(_ as String) >> mockResSet
}
and:
def mockConn = Mock(Connection) {
createStatement() >> mockStatement
}
when: "SqlTable object"
def table = new SqlTable(mockConn, "some query")
then: "the table contains the categorical column"
table.columns[0].getName() == COL_NAME
}
However the test fails. By debugging, I found that, in the SqlTable constructor, the mock for the ResultSetMetaData object, when getColumnName() is called, always returns null.
I did some digging, and it seems like this is due to how stubbing and mocking are handled together by Spock. I found two promising answers on SO:
Spock mock returns wrong value
Mocked method returns null when invocations are checked as well
However for the life of me I wasn't able to modify the test in order to make it work.
The Problem lies in this line getColumnName(_ as int) >> COL_NAME
it works when you change it to getColumnName(_ as Integer) >> COL_NAME or just getColumnName(_) >> COL_NAME.
My current assumptions as to why this is happening, is that the actual arguments in the mock are passed as an Object[] and that can't contain primitive types.
Here is a runnable reproducer
import spock.lang.*;
import java.sql.*;
class ASpec extends Specification {
def "initialising a SQL table"() {
given:
def COL_NAME = "someColumnName"
def mockResSetMeta = Mock(ResultSetMetaData) {
getColumnCount() >> 1
getColumnName(_ as Integer) >> COL_NAME
}
and:
def mockResSet = Mock(ResultSet) {
getMetaData() >> mockResSetMeta
}
and:
def mockStatement = Mock(Statement) {
executeQuery(_ as String) >> mockResSet
}
and:
def mockConn = Mock(Connection) {
createStatement() >> mockStatement
}
when: "SqlTable object"
def table = new SqlTable(mockConn, "some query")
then: "the table contains the categorical column"
table.columns[0].getName() == COL_NAME
}
}
#groovy.transform.Canonical
class Column {
String name
}
class SqlTable {
def columns
public SqlTable(Connection conn, String query) throws Exception {
def statement = conn.createStatement();
def resultSet = statement.executeQuery(query);
def meta = resultSet.getMetaData();
int n = meta.getColumnCount();
columns = new Column[n];
for (int c = 0; c < n; c++) {
columns[c] = new Column(meta.getColumnName(c+1));
// ...
}
}
}
Try it in the Groovy Web Console
For future reference, I easily detect the issue by actually performing mocking and not just stubbing.
then:
1 * mockResSetMeta.getColumnName(_ as int) >> COL_NAME
will print
1 * mockResSetMeta.getColumnName(_ as int) >> COL_NAME (0 invocations)
Unmatched invocations (ordered by similarity):
1 * mockResSetMeta.getColumnName(1)
One or more arguments(s) didn't match:
0: argument instanceof int
| | |
| false int
1 (java.lang.Integer)
I created a reactive resteasy service with Quarkus and Mutiny. In POST method I insert object in my PostgreSQL table and get an Uni< id > in return. I need to set this id as part of my Response class which contains f.ex. "id", "errorCode", "errorString" and other variables, but I struggle as id comes as Uni object and I don't know how to extract id from it.
Create method:
public static Uni<Long> create(PgPool client, RetailPlace retailPlace) {
return client.withTransaction(conn -> conn
.preparedQuery("INSERT INTO retail_place (title) VALUES ($1) RETURNING id")
.execute(Tuple.of(retailPlace.getRetailPlaceTitle()))
.onItem().transformToUni(id -> conn.
preparedQuery("INSERT INTO retail_place_address (retail_place_id,region_code,city,locality_id,apartment,house,region,street) " +
"VALUES ($1,$2,$3,$4,$5,$6,$7,$8) returning retail_place_id")
.execute(Tuple.tuple(Arrays.asList(id.iterator().next().getLong("id"), retailPlace.getRetailPlaceAddress().getRegionCode(),
retailPlace.getRetailPlaceAddress().getCity(), retailPlace.getRetailPlaceAddress().getLocalityId(),
retailPlace.getRetailPlaceAddress().getApartment(), retailPlace.getRetailPlaceAddress().getHouse(),
retailPlace.getRetailPlaceAddress().getRegion(), retailPlace.getRetailPlaceAddress().getStreet())))))
.onItem().transform(pgRowSet -> pgRowSet.iterator().next().getLong("retail_place_id"));
}
I get long value of id in return. Now I need to return a RetailPlaceResponse with id value in it:
public RetailPlaceResponse(String errorCode, long id, boolean isSuccessful) {
this.errorCode = errorCode;
this.id = id;
this.isSuccessful = isSuccessful;
}
If you need both successful and non-successful responses to map to the same class, you could write separate transforms. One for success and one for failure.
public static Uni<RetailPlaceResponse> create(PgPool client, RetailPlace retailPlace) {
return client.withTransaction(conn -> conn
.preparedQuery("INSERT INTO retail_place (title) VALUES ($1) RETURNING id")
.execute(Tuple.of(retailPlace.getRetailPlaceTitle()))
.onItem().transformToUni(id -> conn.
preparedQuery("INSERT INTO retail_place_address (retail_place_id,region_code,city,locality_id,apartment,house,region,street) " +
"VALUES ($1,$2,$3,$4,$5,$6,$7,$8) returning retail_place_id")
.execute(Tuple.tuple(Arrays.asList(id.iterator().next().getLong("id"), retailPlace.getRetailPlaceAddress().getRegionCode(),
retailPlace.getRetailPlaceAddress().getCity(), retailPlace.getRetailPlaceAddress().getLocalityId(),
retailPlace.getRetailPlaceAddress().getApartment(), retailPlace.getRetailPlaceAddress().getHouse(),
retailPlace.getRetailPlaceAddress().getRegion(), retailPlace.getRetailPlaceAddress().getStreet())))))
.onItem().transform(pgRowSet -> new RetailPlaceResponse(null, pgRowSet.iterator().next().getLong("retail_place_id"), true))
.onFailure().transform(error -> new RetailPlaceResponse(error.toString(), 0, false));
}
I have an AccountCreator class with a create method that takes a DTO with the data needed to create an account. At the beginning there is an attempt to create 2 value objects (UserName and Password), then validate the uniqueness of the user name, create the Account entity which takes these 2 value objects in the constructor and save it in the repo. Of course, errors such as incorrect password length, etc. may be returned. I used Eithers for this and now the question is whether this code is ok or maybe it can be written somehow better?
public Either<Error, AccountDto> create(AccountCreateDto accountCreateDto) {
var errorType = ErrorType.ACCOUNT_PERSISTENCE_ERROR;
var errorMessage = "Not unique user name: " + accountCreateDto.userName;
var error = new Error(errorType, errorMessage);
return UserName
.create(accountCreateDto.userName)
.flatMap(userName ->
userNameUniquenessChecker.isUnique(userName.text) ?
Password
.create(accountCreateDto.password)
.flatMap(password -> {
var createdAccount = new Account(
userName,
password,
AccountStatus.OPEN,
LocalDateTime.now(),
new ArrayList<>()
);
var addedAccount = accountRepository.add(createdAccount);
var accountDto = new AccountDto(
addedAccount.userName.text,
addedAccount.password.text,
addedAccount.status,
addedAccount.creationDate,
(long) addedAccount.tasks.size()
);
return Either.right(accountDto);
}) : Either.left(error));
}
The customary FP approach would be to use a Validation style applicative functor - there's one in Vavr called Validation. You can use Validation.combine to combine multiple Validation values into one, e.g.:
public Validation<Seq<<Error>, AccountDto> create(AccountCreateDto accountCreateDto) {
Validation<Seq<<Error>, Account> validAcc =
Validation.combine(
UserName.create(accountCreateDto.userName),
Password.create(accountCreateDto.password)
).ap((un, pw) -> new Account(
un,
pw,
AccountStatus.OPEN,
LocalDateTime.now()
);
Validation<Seq<<Error>, Account> validAcc2 =
validAcc.flatMap(acc -> validateUserIdIsUnique(acc));
Validation<<Seq<Error>, AccountDto> validAccDto =
validAcc2.map(accountRepository::add)
.map(addedAccount ->
new AccountDto(
addedAccount.userName.text,
addedAccount.password.text,
addedAccount.status,
addedAccount.creationDate,
(long) addedAccount.tasks.size()
)
);
}
return validAccDto;
}
private static final var errorType = ErrorType.ACCOUNT_PERSISTENCE_ERROR;
private static final var errorMessage = "Not unique user name: " + accountCreateDto.userName;
private static final var error = new Error(errorType, errorMessage);
Validation<Seq<Error>, Account> validateUserIdIsUnique(Account acc) {
return userNameUniquenessChecker.isUnique(acc.userName.text) ?
Validation.valid(userName) :
Validation.invalid(error);
}
You can elide the temporaries - validAcc, validAcc2 & validAccDto, however I left them in for clarity.
(caveat emptor - haven't tested that this code works, or even compiles)
I have a dataframe a2 written in scala :
val a3 = a2.select(printme.apply(col(“PlayerReference”)))
the column PlayerReference contains a string.
that calls an udf function :
val printme = udf({
st: String =>
val x = new JustPrint(st)
x.printMe();
})
this udf function calls a java class :
public class JustPrint {
private String ss = null;
public JustPrint(String ss) {
this.ss = ss;
}
public void printMe() {
System.out.println("Value : " + this.ss);
}
}
but i have this error for the udf :
java.lang.UnsupportedOperationException: Schema for type Unit is not supported
The goal of this exercise is to validate the chain of calls.
What should I do to solve this problem ?
The reason you're getting this error is that your UDF doesn't return anything, which, in terms of spark is called Unit.
What you should do depends on what you actually want, but, assuming you just want to track values coming through your UDF you should either change printMe so it returns String, or the UDF.
Like this:
public String printMe() {
System.out.println("Value : " + this.ss);
return this.ss;
}
or like this:
val printme = udf({
st: String =>
val x = new JustPrint(st)
x.printMe();
x
})
i'm trying to extract data from the ANTLR parse tree, but not fully grasping how this should be done correctly
Let's say i have the following two SQL queries:
// language=SQL
val sql3 = """
CREATE TABLE session(
id uuid not null
constraint account_pk
primary key,
created timestamp default now() not null
)
""".trimIndent()
// language=SQL
val sql4 = """
CREATE TABLE IF NOT EXISTS blah(
id uuid not null
constraint account_pk
primary key,
created timestamp default now() not null
)
""".trimIndent()
Now i parse both of them:
val visitor = Visitor()
listOf(sql3, sql4).forEach { sql ->
val lexer = SQLLexer(CharStreams.fromString(sql))
val parser = SQLParser(CommonTokenStream(lexer))
visitor.visit(parser.sql())
println(visitor.tableName)
}
In my visitor if i visit the tableCreateStatement, i get the parse tree, but obviously just grabbing child1 will work for sql3, but not for sql4 since child1 in sql4 is IF NOT EXISTS
class Visitor : SQLParserBaseVisitor<Unit>() {
var tableName = ""
override fun visitCreate_table_statement(ctx: SQLParser.Create_table_statementContext?) {
tableName = ctx?.getChild(1)?.text ?: ""
super.visitCreate_table_statement(ctx)
}
}
Is there a way to find a specific token in the parse tree?
I'm assuming the payload has something to do with it, but since it's of type Any, i'm not sure what to check it against
override fun visitCreate_table_statement(ctx: SQLParser.Create_table_statementContext?) {
ctx?.children?.forEach {
if (it.payload.javaClass == SQLParser::Schema_qualified_nameContext) {
tableName = it.text
}
}
super.visitCreate_table_statement(ctx)
}
EDIT: the .g4 files are from
https://github.com/pgcodekeeper/pgcodekeeper/tree/master/apgdiff/antlr-src
this seems to work
override fun visitCreate_table_statement(ctx: SQLParser.Create_table_statementContext?) {
ctx?.children?.forEach {
if (it.payload.javaClass == Schema_qualified_nameContext::class.java) {
tableName = it.text
}
}
super.visitCreate_table_statement(ctx)
}
For branching trees
fun walkLeaves(
childTree: ParseTree = internalTree,
leave: (childTree: ParseTree) -> Unit) {
if (childTree.childCount == 0) {
if (!childTree.text?.trim().isNullOrBlank()) {
leave(childTree)
}
} else {
for (i in 0 until childTree.childCount) {
walkLeaves(childTree = childTree.getChild(i), leave = leave)
}
}
}
fun extractSQL(
childTree: ParseTree,
tokens: MutableList<String> = mutableListOf()
): String {
walkLeaves(childTree = childTree) { leave ->
tokens.add(leave.text)
}
...
}