I've been struggling with how to map to object with generics in Dozer and have not found anything to aid me after extensive searching.
I'm trying to map some objects that I wrote to some that were created by someone else. I started out using annotations and saw a similar error so I moved to using a mapping file instead. However, it doesn't seemed to have helped.
Here's an example of what I'm trying to accomplish.
public interface MyObject<T extends MyContent> {
T getData();
}
public interface MyStaff extends MyContent {
Demo getDemo();
}
public interface Demo {
Name getName();
}
public interface Name {
String getFirstName();
String setFirstName(String firstName);
}
<mapping>
<class-a>com.example.Staff</class-a>
<class-b>com.example.MyObject</class-b>
<field>
<a>firstName</a>
<b>data.demo.name.firstName</b>
</field>
</mapping>
Here's the error I'm seeing:
org.dozer.MappingException: No read or write method found for field (data.demo.name.firstName) in class (interface com.example.MyObject)
I've tried another of permutations (using concrete classes rather than interfaces, for example) and nothing works.
I was able to get this working. It seems the issue was due to the fact that the implementation class was not public. It seems like this might be a bug or at least an enhancement. Dozer should either notify you the class isn't accessible as suggesting the field isn't found is misleading.
Related
I am running into the following error while trying to read an object list from a MongoDB collection in Java while applying a filter:
Encoding a Class: '...DefaultDiscussionThreadConfiguration' failed with the following exception:
Class contains generic types that have not been specialised.
Top level classes with generic types are not supported by the PojoCodec.
The strange thing is: The referenced class does not contain a generic type, and should not actually exist in the context where that error occurs. Here is what this class looks like:
public class DefaultDiscussionThreadConfiguration extends DiscussionThreadConfiguration {
#Override
#BsonIgnore
public DiscussionThreadConfigurationType getDiscussionThreadConfigurationType() {
return DiscussionThreadConfigurationType.DEFAULT;
}
#Override
public DefaultDiscussionThreadConfigurationNetworkData toDiscussionThreadConfigurationNetworkData() {
return new DefaultDiscussionThreadConfigurationNetworkData();
}
}
And even the extended class does not contain Generic Types.
#BsonDiscriminator
public abstract class DiscussionThreadConfiguration {
public abstract DiscussionThreadConfigurationType getDiscussionThreadConfigurationType();
public abstract DiscussionThreadConfigurationNetworkData toDiscussionThreadConfigurationNetworkData();
}
A bit of context:
What I am trying to do is to get a list of all Default type discussion thread database objects. These look like this:
DiscussionThreadDbData
├discussionThreadConfiguration
└[other fields]
DiscussionThreadConfiguration meanwhile is a field that determines the thread type, and also holds type-specific data. Its inheritance structure looks like this:
DiscussionThreadConfiguration (abstract)
├DefaultDiscussionThreadConfiguration
└PrdDiscussionThreadConfiguration (abstract)
├PrdDiscussionThreadGlobalConfiguration
└PrdDiscussionThreadInternalConfiguration
Now, as for why I said that the DefaultDiscussionThreadConfiguration should not even exist in that context:
This happens in the course of a test where I add a discussion PrdDiscussionThreadGlobalConfiguration, then run a query that is supposed to only return default threads, and expect to have 0 threads returned. When I pause the test before it crashes and check the database, I can see that I have a thread in there that looks like this:
discussion_thread[0]
├discussionThreadConfiguration
│├_t:...PrdDiscussionThreadGlobalConfiguration
│└[other fields]
└[other fields]
Which is correct. Now the filter I run on this is:
Filters.eq(
discussionThreadConfiguration._t,
DefaultDiscussionThreadConfiguration.class
);
But that doesn't work for some reason.
The actual error message is somewhat misleading. The reason why this happens is that you try to pass the filter a class instead of a string, which causes weird behaviour.
The filter actually has to look like this:
Filters.eq(
discussionThreadConfiguration._t,
DefaultDiscussionThreadConfiguration.class.getName()
);
assuming I have the following entities:
#Entity
public class Word { ... }
#Entity
public class Noun extends Word { ... }
#Entity
public class Verb extends Word { ... }
(Plus the usual Disriminator- and Join-Strategy stuff, simply assume that the entities work fine, which they do.)
I tried ...
public interface WordRepository extends CrudRepository<Word, Long>{
#Query("SELECT x FROM Word x WHERE type(x) = ?1")
<T extends Word> List<T> findByClass(Class<T> clz);
}
...but this gives me an exception, caused by:
org.hibernate.QueryException: Not all named parameters have been set: [1] [SELECT x FROM Word x WHERE type(x) = ?1]
One solution is to replace Class<T> with Class<?>, then the code works, but obviously that's not type-safe anymore since then I can write...
List<Verb> verbs = repository.findByClass(Noun.class);
...which runs, but obviously throws a ClassCastException whenever I try to access the verbs (since all the objects in the list are actually Nouns, not Verbs)
Is there any way to write this type-safe with spring-data, preferably without hardcoding all types into their own methods (findNouns, findVerbs, etc.) or defining Repositories for all types?
Edit: The problem seems to be the Parameter.isDynamicProjectionParameter(MethodParameter) method, that seems to define a special behavior for Class<T> parameters, so they are only used for dynamic parameters but cannot be given into the query itself. Hm. Wish anyone had a way around that.
I just faced the same issue. For your/every other user´s information i filed a spring data jpa issue, which you folks can follow here:
https://jira.spring.io/browse/DATAJPA-1257
I created a DAL few weeks ago which connects to Mongo Database.
When I want to query the database with a certain class, I need to know collection it belongs.
I thought about creating an annotation, that I'll put above each class which will contain the name of the related collection, and when I'll need to query the database I'll get the annotation value by reflection.
My question is how can I declare that the class that is sent to me has the annotation.
Pretty much like:
public List<T> query(Class<T extends Interface>)
only:
public List<T> query(Class<T has Annotation>)
Thanks.
You should either use interface or enumeration to do this. It is much simpler and more explicit.
But, if you want to experiment it is fine. Following should work
public List query(Class klass) {
for(Annotations a : klass.getAnnotations()) {
//Iterate and do stuff
}
//do other stuff
}
I'm trying to persist some enums in Hibernate and it looks like my two options for built in support are to use the name of the enum, which I would rather not do because it's string based instead of int based, or the ordinal of the enum, which I would rather not do because if I add one of the enum values at the top of the class later on, I break everything down the line.
Instead, I have an interface called Identifiable that has public int getId() as part of its contract. This way, the enums I want to persist can implement Identifable and I can know that they'll define their own id.
But when I try to extend EnumValueMapperSupport so I can utilize this functionality, I'm greeted with errors from the compiler because the EnumValueMapper interface and the EnumValueMapperSupport class are not static, and thus are expected to be locked into a given EnumType object.
How can I extend this functionality in Hibernate, short of rewriting a bunch of Hibernate code and submitting a patch. If I can't, is there another way to somehow store an enum based on something other than the ordinal or name, but instead on your own code?
In a related thought, has anyone personally been down this road and decided "let's see how bad the name mapping is" and just went with name mapping because it wasn't that much worse performance? Like, is it possible I'm prematurely optimizing here?
I'm working against Hibernate version 5.0.2-final.
At least for Hibernate 4.3.5 the EnumValueMapper is static - although private.
But you can extend EnumValueMapperSupport in an extension of EnumType:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
}
To create an instance of this mapper you need an instance of your EnumType:
ExampleEnumType type = new ExampleEnumType();
ExampleMapper mapper = type.new ExampleMapper();
Or you create it inside your type:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
public ExampleMapper createMapper() {
return new ExampleMapper();
}
}
How can I keep my using interfaces in classes I want to use JiBX binding with?
Example:
I have this very simple model in java:
public interface A {
B getB();
void setB(B b);
}
public interface B {
String getData();
void setData(String data);
}
public class AImpl implements A {
B b;
#Override
public B getB() {
return b;
}
#Override
public void setB(B b) {
this.b = b;
}
}
public class BImpl implements B {
private String data;
#Override
public String getData() {
return data;
}
#Override
public void setData(String data) {
this.data = data;
}
}
And this binding document:
<binding>
<mapping name="A"
class="com.test.AImpl">
<structure name="B" usage="optional" get-method="getB" set-method="setB"/>
</mapping>
<mapping name="B"
class="com.test.BImpl">
<value name="data" set-method="setData" get-method="getData" usage="optional"/>
</mapping>
</binding>
When I try to run my code I get this exception:
java.lang.ClassFormatError: Method
in class com/test/B has illegal
modifiers: 0x1001
I've tried to use 'abstract="true"' on both mapping, only to get this exception:
...Caused by:
org.jibx.runtime.JiBXException: Unable
to access binding information for
class com.test.A Make sure the binding
has been compiled...
The only solution I've found is to have AImpl hold a BImpl instead of a B, and have the getter return BImpl and the setter recieve BImpl. This is very wrong as it breaks the interface completely.
How can I solve this? I've been pulling hairs out, having tantrums (the real issue is much more complex, and JiBX cryptic error messages don't help) - nothing help.
Is this solvable? Is JiBX really that intrusive (in that it requires me to abandon all interface programming?)
Please don't answer "use AbstractB" as it's the same problem, only one level removed.
In the mapping, you should be able use the "create-type" attribute to specify the concrete class that JiBX should instantiate for bean properties that have an interface type. I use this a lot for collection properties. For example, you can tell JiBX to instantiate a java.util.HashSet for a property of type java.util.Set. But I believe it works just as well for non-collection properties. Your mapping would look something like:
<mapping class="com.mypackage.AImpl" name="A">
<structure get-method="getB" set-method="setB" create-type="com.mypackage.BImpl">
...
</structure>
...
</mapping>
JiBX will call the no-arg constructor to create the B object. Alternatively, you could use a factory or a custom serializer/deserializer if you need fancy instantiation logic. See this reference page for details.
Another good resource is the binding.dtd - apparently it's not in the distribution but can be downloaded from here: http://jibx.cvs.sourceforge.net/viewvc/checkout/jibx/core/docs/binding.dtd. Put this file somewhere (c:\binding.dtd for example). Then, in the top binding entry, use this:
<binding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file://jibx/binding.dtd">
and register file://jibx/binding.dtd to point to your saved binding.dtd for documentation and verification goodies.
It's amazing what inertia does - I know that xml files should have schemas / dtds, I've used them before and always said "without a schema understanding this would've been impossible". Yet when I've entered this project, it never occurred to me to search for the schema / dtd for this xml - I just accepted it as given that it had none.
Lesson learned.