org.dbunit.dataset.NoSuchColumnException - java

I'm getting the following error when I run my tests:
org.dbunit.dataset.NoSuchColumnException: myTable.MYFIELD - (Non-uppercase input column: myfield) in ColumnNameToIndexes cache map. Note that the map's column names are NOT case sensitive.
at org.dbunit.dataset.AbstractTableMetaData.getColumnIndex(AbstractTableMetaData.java:117)
I set a breakpoint in org.dbunit.dataset.AbstractTableMetaData#getColumnIndex and discovered the following. In IntelliJ Idea the method looks like this:
public int getColumnIndex(String columnName) throws DataSetException
{
logger.debug("getColumnIndex(columnName={}) - start", columnName);
if(this._columnsToIndexes == null)
{
// lazily create the map
this._columnsToIndexes = createColumnIndexesMap(this.getColumns());
}
String columnNameUpperCase = columnName.toUpperCase();
Integer colIndex = (Integer) this._columnsToIndexes.get(columnNameUpperCase);
if(colIndex != null)
{
return colIndex.intValue();
}
else
{
throw new NoSuchColumnException(this.getTableName(), columnNameUpperCase,
" (Non-uppercase input column: "+columnName+") in ColumnNameToIndexes cache map. " +
"Note that the map's column names are NOT case sensitive.");
}
}
The value of this.getColumns() does not contain any Column with Column.columnName matching the parameter columnName. Therefore colIndex becomes null and the exception is thrown.
It looks like DBUnit is looking for the column index in the wrong table meta data.
How can I fix this?
Note: I inherited this code from someone else (didn't write it).

I'm sensitive to the fact that you can't really share code. That does make things a little difficult, but here's an answer I think is reasonable given the confines:
I was able to easily reproduce this exception using a minimal Spring Boot/DbUnit project cloned from GitHub. Perhaps my observations will amount to the hint you're looking for, or at least inspire a better answer.
Steps
Clone the project and install dependencies.
Run the HsqldbexampleApplicationTests.contextLoads() test. It passes.
Get into StaticResource.java, and change one of the #Column annotations.
For example, I changed:
#Column(name = "CONTENT")
private String content;
to:
#Column(name = "CONTENTZ")
private String content;
Run the test again and observe the exception
Alternatively, you can get into sampleData.xml and change the CONTENT attributes there (the attribute name), to produce the same exception.
Observations
The test gets its data from /META-INF/dbtest/sampleData.xml. Note the CONTENT attribute.
The resource has an #Column annotation whose name must match an attribute found in the sampleData.xml elements.
Since your trouble is also with running tests, it may be that your code and the .xml(?) that hydrates your test data store are simply out of sync with respect to a column name.
Further implication of an XML file?
My attempts to provoke this exception by changing queries and instance variable names were unsuccessful. Everything I tried made the compiler complain, so I ruled it out.
For example, I also checked out this repo, and tried to change a query and an instance variable, but was thwarted by the compiler at every step. Changing a query:
Changing an instance variable name:
Where to look
Anywhere in any java code where you have #Column with MYFIELD inside it. Remember, annotations can span several lines in a file.
Any xml files containing MYFIELD.
Assuming the code under test works fine, and your problems are confined to running tests, the mechanism that injects data into your test is the prime suspect. If this isn't an xml file, what is it?

It's not clear from you post how do you get the _columnsToIndexes
It looks like a piece of some reflection code that depends on your POJO.
In this case the problem migth be in Lazy initialization of the object. Lazy initialized objects are not just entity object but some kind of proxy and attemption of getting its properties through the reflection may cause this problem.
Probably you should try add some kind of unproxy method into you createColumnIndexesMap. Here is example:
public static <T> T initializeAndUnproxy(T entity) {
if (entity == null) {
throw new InternalServerException("Entity passed for initialization is null");
}
T unproxy = entity;
Hibernate.initialize(entity);
if (isProxy(entity)) {
unproxy = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
}
return unproxy;
}
public static <T> boolean isProxy(T entity) {
return entity instanceof HibernateProxy;
}
of course it depends on your ORM, here is example for Hibernate

Related

Using Lombok's SuperBuilder with Hibernate Validator (jakarta.validation.x) annotation on a container type leads to "Type mismatch"

I have a class ActivitiesModel which uses Lombok's SuperBuilder.
import jakarta.validation.NotBlank;
// other imports and statements omitted for brevity.
#Data
#SuperBuilder
#NoArgsConstructor
public class ActivitiesModel {
public static final String ACTIVITIES_NOT_NULL_MESSAGE = "Activities cannot be null";
public static final String ACTIVITY_NOT_BLANK_MESSAGE = "Activity cannot be blank";
#NotNull(message = ACTIVITIES_NOT_NULL_MESSAGE)
private List<#NotBlank(message = ACTIVITY_NOT_BLANK_MESSAGE) String> activities;
}
I am using this builder to create an object of ActivitiesModel, and then validating it using Hibernate's Validator interface:
// Somewhere else in the application.
// Create an object using the builder method.
ActivitiesModel activitiesModel = ActivitiesModel.builder()
.activities(List.of("hello", "world")) // <----- Point A
.build();
// Validate the object using Hibernate's validator.
validator.validate(activitiesModel);
However, running this code gives me the following error:
java.lang.Error:
Unresolved compilation problem:
Type mismatch: cannot convert from List<String> to List<E>
The stack trace seems to be pointing at Point A.
I have tried the following approaches:
Replacing the #SuperBuilder with #Builder and #AllArgsConstructor.
Replacing the message attribute with a string literal instead of a static final variable, i.e:
private List<#NotBlank(message = "Activity cannot be blank") String> activities;
1st approach seems to fix this error, however, it's not something I can use as I need to extend the builder functionality to a subclass of ActivitiesModel. Also, this issue is also present in another abstract class, so the super builder functionality for parent classes is definitely required.
2nd approach also works in solving the error. However, going with it is a bit problematic because I then need to have the same message string in the validation test for this model class, which is something I would like to avoid as it duplicates the string.
Another thing to note is that this error only seems to occur in the presence of an annotation on the generic type parameter of the container, which is NotBlank in this case. It is not influenced by any annotations which are present directly on the field itself (NotNull in this case).
So, all in all, these are the questions that I would like to get some answers to:
Somehow, Lombok is able to figure out the types in case of a string literal but not in case of a static final String. Why is that?
Am I going about this totally wrong? The problem occurs because I'm trying to store the message string in a variable, and I'm trying to re-use the same variable at two places: the annotation's message attribute, and in the validation test for the model class. Should I not be checking for the presence of the message in my validation tests, but be checking for something else instead?
For anyone who comes across this later on, the research for this issue has led me to believe that comparing message strings in tests is not the way to go about writing validation test cases. Another downside to this approach is that you might have different validation messages for different locales. In that case, the message string itself might be a template e.g. my.message.key with its values in a ResourceBundle provided to Hibernate, i.e. files such as ValidationMessages.properties and ValidationMessages_de.properties.
In such a scenario, you could compare message for one locale in your validation test case, however, a better approach might be to check the annotation and the field for which the validation has failed. We can get both of these pieces of information via the ConstraintViolation and subsequently the ConstraintDescriptor types, provided by Hibernate. This way we can circumvent checking the message itself, but rely on the actual validation annotation which has failed.
As for the solution to this question, it seems it was a build cache issue. Cleaning maven's build cache results in this code working perfectly fine, but VSCode still seems to have an issue. For now, I will choose to ignore that.

Use CriteriaBuilder to filter IN multiple ENUM values

I want to construct a query with CriteriaBuilder, and to add a Predicate into the where instruction, to filter one of my object field from a potential list of ENUM values.
Despite this similar post : Filtering data with CriteriaBuilder to compare enum values with literals not working
I didn't managed to make my code works. Here is the "simplified" object:
//BUNCH OF ANNOTATIONS
public class Action {
#Column(name = "CONTEXT")
#Enumerated(EnumType.STRING)
private ActionContext context = ActionContext.SALE;
// ...
}
My enum is:
public enum ActionContext {
SALE,
ORDER,
OTHER
}
And in my filters, I can for example receive something like "SALE,ORDER".
So I created a custom Specification, and when I construct it, I'm doing this :
private List<Predicate> filters = new ArrayList<>();
//...
// filterValue is a String, it can be "SALE,ORDER" for ex.
case CONTEXT_FILTER_NAME:
if (filterValue != null && !filterValue.isEmpty()) {
String[] contextTokens = filterValue.split(",");
CriteriaBuilder.In<String> inClause = cb.in(root.get(CONTEXT_FILTER_NAME));
Arrays.asList(contextTokens).forEach(inClause::value);
filters.add(inClause);
}
break;
The part after is just doing the query, it works for other filters ... but the fact my entity have an enum : when I run a test with these filters (sending "SALE,ORDER" filter for ex), I get the following error :
Parameter value [SALE] did not match expected type
[com.mycompany.domain.enums.ActionContext
I also tried by replacing CriteriaBuilder.in by .or() and putting in the collection of parsed tokens, but I had the same error.
And yes, I'm sure it's not a parsing error, because when I put a breakpoint in this swich/case section, I see the correctly constructed contextTokens array with good value in it. And in the Specification 'in' I can see the correct values being set.
Does anybody know what I'm missing ? thx a lot
OK I was comparing Enum with String, and I didn't see this ...
So you just have in this cases to convert ActionContext ENUM with ActionContext.valueOf(token) or on the other way around, converting the root.get(CONTEXT_FILTER_NAME).as(String.class).
Hope it helps people, that are like me, sometimes in a kind of tunnel vision.

Eclipse, Java: Generate class field from static string or vice versa

I am working on a JAVA web service with mongo, In order to implement mongo java driver POJO api (like Morphia), I establish my POJO like the following:
public class User {
public static final String USER_NAME = "userName";
private String userName;
public User() {
}
// getter && setter //
}
USER_NAME = "userName" is a reference for future use as a filed name. What I'm trying to achieve is that I could use a simple if to check if received data equals to the field name. For example :
User user = new User();
String receivedData = httpRequest.getParameter(User.USER_NAME);
if (receivedData == null) {
return null;
} else {
user.setUserName(receivedData);
userCollection.insertOne(user);
// userCollection is MongoCollection<User>
return Gson().toJson(user);
}
I am looking for a eclipse function or plug in that could auto generate one of the declaration (USER_NAME = "userName" and private String userName) by the other and make sure the consistency of the code.
Of course, that would be appreciated if there's any suggestion for a better practice.
EDIT
Stephan's reflection approach gives a great flexibility for the code. However, compare to my original simplified example, the real situation might be more complex. For example, one httpRequest has many different parameters which are stored in different(n) POJO and each POJO has many different(n) fields. In that case, we will do n*n loop for just getting the field value.
If you want to do the same for all fields of the POJO, consider iterating the result of User.class.getFields(). For each Field get the name (f.getName()), use the name to retrieve the value and set it to the field (f.set(object,value)). With this approach there's no need for a constant like USER_NAME.
EDIT 1: If performance is an issue you may of course collect all those fields into some collection up-front once and for all, but then the bottleneck will remain at having to try httpRequest.getParameter() for each possible field. Nothing specific to the reflective approach.
EDIT 2: In a "nicer" language there could be type checked syntax for retrieving a Field instance, e.g., imagine (not Java):
Field f = User::userName;
This answer basically demonstrates that generating redundant constants is not strictly necessary, since the thing that consistently connects the name to the field already exists, it's a Field.

Is there a better way of obtaining an object's field getters other than java reflection api or i am misusing PropertyDescriptor's getReadMethod?

Context:
I am building an Excel document in a generic way with data i receive from a SOAP service endpoint. I receive the data as a List and i have the model (JavaBeans) for every Object i receive according to the method called.
So I set the first row of the sheet as the header from the object's fields (getDeclaredFields).
Then i go on filling up the column row by row with values from the list of objects.
The problem:
I haven't found a workable way of getting the object's field values.
I have tried using the getters with the java reflection API with something like this answer's https://stackoverflow.com/a/5503534/4807777 findGetterName , findGetter however the PropertyDescriptor's getName sometimes is a different letter case from the field name as obtained from the class's getDeclaredFields.
Let's say i overcome this by capitalizing both names, the getReadMethod stil fails - doesn't seem to find getters for the fields which use the is prefix (i.e boolean fields). I don't know if i am misusing it or it is a bug (debugging the getReadMethod appears to only work with the get prefix, even though it appears to handle the is prefix case for booleans).
Considering the fact the fields aren't accesible outside of the object's package, therefore solely through invoking getters.
Is there a better way of obtaining the object's field getters or i am missing something with the getter methods?
Update: Spring's BeanUtils seems to be better for getting the properties with it's getPropertyDescriptors is better than java Class's getDeclaredFields, when the JavaBean properties are mapped to XML elements.
This fixes the different letter cases situation. However it stil doesn't find it's readMethod when not using the get prefix.
Edited - to show an example of getReadMethod not finding the is prefixed getter, as Laszlo Lugosi requested.
A simple class:
class Test {
private String assignmentType;
private Boolean conserved;
public String getAssignmentType() {return assignmentType;}
public void setAssignmentType(String assignmentType) {this.assignmentType = assignmentType;}
public Boolean isConserved() {return conserved;}
public void setConserved(Boolean conserved) {this.conserved = conserved;}
}
Run this with the findGetter and findGetterName written in the answer linked above:
{
Test obj = new Test();
obj.setAssignmentType("someType");
obj.setConserved(true);
Field[] fields = obj.getClass().getDeclaredFields();
String fieldName;
for (int i=0;i<fields.length;i++){
fieldName = fields[i].getName();
java.lang.reflect.Method method;
Object val = null;
try {
method = obj.getClass().getMethod(findGetterName(obj.getClass(),fieldName));
val = method.invoke(obj);
}
catch (Exception e){
e.printStackTrace();
}
}
}
Edited 2
While i could simply write a getReadMethod following the convention Laszlo Lugosi highlighted i do prefer finding an API for handling accessors.
As you know only the object field name, and JavaBean has convention, you can figure out the getters easily. The rules are getUpperfieldname() and isUpperfieldname if field is boolean. And you can find out the return type as well from the object field.

Java reflection querying annotations by type

I'm trying to query the annotations from a class using this code:
for (final Annotation annotation : annotations) System.out.println(annotation);
final JsonSchema[] schemas = clazz.getAnnotationsByType(JsonSchema.class);
if (schemas.length == 0) {
throw new IllegalArgumentException("No JsonSchema annotation found.");
}
If I arrive here via a unit test I get past the schemas.length test. If I do so via a Maven plugin which passes the URL of a class into URLClassLoader().loadClass() then it throws IllegalArgumentException. However, it also prints out:
#mypackage.JsonSchema()
i.e. The annotation appears to be there if I loop through the results of getAnnotations() but isn't found by getAnnotationsByType. What could be causing this?
Edit: If I try looping through and comparing the canonical names then casting to JsonSchema it won't let me as it appears to be a com.sun.proxy which is not an instanceof JsonSchema.
Edit: It's because they're in different class loaders, I'm sure. Quite how to fix that...
Got it.
I was passing an array of URLs for classes to load to new URLClassLoader(). By adding a second parameter of Thread.currentThread().getContextClassLoader() to the constructor it seems to load them into the same ClassLoader and everything then works as expected.

Categories