Cannot find field in mongo collection using morphia - java

I have an abstract Scala class as a Mongo collection.
#Entity("aclTemplate")
abstract class AclTemplate(#(Id#field) var id: String) extends Serializable
Another class extends the above
#Entity("aclTemplate")
class GroupACLTemplate(id: String, var groupRoleAccess: Set[GroupRoleAccess]) extends AclTemplate(id) with Serializable
There are some docs of GroupACLTemplate in the collection. I am trying a simple query
createQuery().disableValidation().field("groupRoleAccess.groupId").equal(groupId).asList();
This throws a ValidationException
org.mongodb.morphia.query.ValidationException: The field 'groupRoleAccess.groupId' could not be found in 'com.model.acl.AclTemplate'
I do not think it is because of the long standing polymorphism issue in morphia. Because when I try to access just groupRoleAccess, it is able to. However, it is not able to access inside that set. It is a normal Java set. This is the GroupRoleAccess class
class GroupRoleAccess(var groupId: String, var roleId: String) extends Serializable
Am I missing something here?

I managed to hack something up. Apparently, since the collection is an abstract class, Mongo/Morphia does not look for attributes that are present in its subclasses. So I used createQuery and passed the class of the subclass.
ds.createQuery(clazz).disableValidation().field("groupRoleAccess.groupId").equal(groupId).asList();
But I still wonder how it was able to extract groupRoleAccess before

You should try 1.3.0-SNAPSHOT. I just fixed a bug similar to this and it probably fixes your issue, too.

Related

Inheriting class with primary constructor

I have a parent class as following,
interface ITask { }
open class Task(val targetServer: Server) : ITask { }
Then there a child inheriting it and overriding the primary constructor as following,
data class FileTask(val sourceServer: Server, targetServer: Server) : Task(targetServer = targetServer) {
}
This is throwing a compilation error in eclipse as
Data class primary constructor must have only property (val / var) parameters
Removing the data keyword from the class header will kill the error, but I don't understand why.
Keeping the data keyword and adding var to the targetServer gives another error
'targetServer' hides member of supertype 'Task' and needs 'override' modifier
Adding override to the targetServer to be override var targetServer: Server throws another error
'targetServer' in 'Task' is final and cannot be overridden
I need some help to understand these errors.
The initial error is because a data class can't have parameters in its primary constructor other than val or var properties. Removing the data keyword lifts this restriction.
It's been mentioned that data classes generally don't play well with inheritance. They're supposed to be used as simple data transfer objects, and aren't really suitable for participating in hierarchies, because it becomes hard to understand which properties are going to be considered in the implementations of the generated methods. Your best bet might be to not use them at all here.
For more about data classes and inheritance, here is the proposal that was implemented in Kotlin 1.1.
To get back to the specific problem, if you really have to make this class a data class, you can mark the property in the base class as open and then override it in FileTask, like so:
open class Task(open val targetServer: Server) : ITask
data class FileTask(val sourceServer: Server, override val targetServer: Server): Task(targetServer = targetServer)
This basically hides the property declared in Task, and always accesses the property in FileTask instead.
I don't know what your exact requirements for your classes are, but one thing you could do to clean this up and make it a bit nicer would be to make Task and its targetServer property abstract, like so:
abstract class Task : ITask {
abstract val targetServer: Server
}
data class FileTask(val sourceServer: Server, override val targetServer: Server) : Task()
This way you wouldn't have the unnecessary property (and backing field) in the base class, and you'd be forced to have a targetServer property in all the classes that inherit from Task. You could also take this a step further, and put the property in the ITask interface as well.
interface ITask {
val targetServer: Server
}
I know this is a very old post, but I was struggling with the same issue and making my superclass abstract was not a solution.
You just need to do the following:
change this
open class Task(val targetServer: Server) : ITask { }
to (Please note, I have made targetServer variable to open)
open class Task(open val targetServer: Server) : ITask { }

How can I add my own EnumValueMapperSupport

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 to cast dynamically loaded annotation class to Annotation?

I want to achieve the following logic:
Load an annotation class with ClassLoader.loadClass( String )
Check if a given Class object has that annotation using Class.isAnnotationPresent(Class<? extends Annotation>)
However, the return type of loadClass() is Class<?>, which cannot be used in isAnnotationPresent() directly. So, what steps should I take to use the loaded annotation class in isAnnotationPresent()?
This seems a simple problem, but I just fail to find an answer from the mighty Google. :-(
In any case, thank you very much.
You can use Class.asSubclass():
Class<? extends Annotation> a = cl.loadClass(...).asSubclass(Annotation.class);

Cast an object dynamically from a string

I'm writing a servlet-filter as the solution of this question:
Is it a good idea to filter inside a JSF template?
now, the idea is to create a big filter to check all privilegies and give the access or not to a certain user.
I create a Map to contains all privilegies for all sub applications and it has the sub application's id (a Long value) as Key and for the value another Map that contains other important informations.
The controller classes are named class1Controller, class2Controller ecc and the subapplications are stored in many folder named class1, class2 ecc...
The last thing that I must say is that all classes have a parameter called applicationID that is the same key of the Map that I mentioned previously.
So, what I would do?
I can retrieve the subapplication visited by the user using getRequestURI() method from HttpServletRequest, the problem is that I should take the application id from the class linked to that application, so I wrote this code:
Long id= ((Class.forName(packageName+applicationName+"Controller"))session.getAttribute(applicationName+"Controller")).getApplicationId();
The problem is that the compiler returns that it can't find method getApplicationId()!
Can I do something to resolve this problem? Or I must find another way to do that?
The last thing that I must say is that all classes have a parameter called applicationID
It sounds like you want an interface with the getApplicationId method in; make all the controllers implement that interface, and then all you need to do is cast to that interface.
// TODO: Work out a better interface name than "Application" :)
Object attribute = session.getAttribute(applicationName+"Controller");
Long id = ((Application) attribute).getApplicationId();
(You might want to use an abstract base class as described by BalusC - they're variations on the same theme, really.)
You're calling getApplicationId() on a Class instance, but it does not have that method at all.
The normal approach is to let all those classes extend some common base abstract class or an interface which has the method definied and then cast to that base abstract class or interface instead.
E.g. with a base abstract class:
public class FooController extends BaseController {}
public class BarController extends BaseController {}
etc..
Where the base abstract class look like this:
public abstract class BaseController {
public Long getApplicationId() {
return applicationId;
}
}
Then you can get it as follows:
Long id = ((BaseController) session.getAttribute(applicationName + "Controller")).getApplicationId();

How to get a properly typed List from a ReflectionDBObject

I have a model classes like
public class MyClass extends ReflectionDBObject {
private List<NiceAttribute> attributes;
...
}
public class NiceAttribute extends ReflectionDBObject {
...
}
I create it in a typesafe way, like
List<NiceAttribute> attrs = new ArrayList<NiceAttribute>();
attrs.add(new NiceAttribute());
MyClass myClass = new MyClass();
myClass.setAttributes(attrs);
then save it to mongo, and retrieve with a code like
DBCollection col = ...;
col.setObjectClass(MyClass.class)
MyClass foundObject = (MyClass)col.findOne();
But the problem is that foundObject's attributes becomes a list of BasicDBObject. Looks like a driver can not (or does not want to) detect a list items type. Is this a driver limitation, or I missed something? What would be an elegant workaround for the problem?
BTW, I know about Morphia etc. Maybe it solves the problem. But my project is tiny, and I don't want to complicate things having one more layer of abstraction.
Well, there is a solution, but you're not gonna like it. Basically you can specify the corresponding class for an internal path within your object. Here's what I did, and it works:
public class Release extends ReflectionDBObject {
//other fields omitted
private List<ReleaseDetailsByTerritory> releaseDetailsByTerritory = new ArrayList<ReleaseDetailsByTerritory>();
}
public class ReleaseDetailsByTerritory extends ReflectionDBObject { //...}
Now, if I simply do this:
releaseColl.setObjectClass(Release.class);
releaseColl.setInternalClass("ReleaseDetailsByTerritory.0", ReleaseDetailsByTerritory.class);
Release r = (Release) releaseColl.findOne();
//the internal list will contain ReleaseDetailsByTerritory type objects (not DBObjects)
System.out.println(r.getReleaseDetailsByTerritory().get(0).getClass().getName());
The crappy thing here is that you can't (or at least I haven't found how) to specify the mapping class for ALL the elements of an embedded array. You CANNOT do something like:
releaseColl.setInternalClass("ReleaseDetailsByTerritory", ReleaseDetailsByTerritory.class);
or
releaseColl.setInternalClass("ReleaseDetailsByTerritory.*", ReleaseDetailsByTerritory.class);
You have instead to specify the mapping class of any possible element of the embedded array:
releaseColl.setInternalClass("ReleaseDetailsByTerritory.0", ReleaseDetailsByTerritory.class);
releaseColl.setInternalClass("ReleaseDetailsByTerritory.1", ReleaseDetailsByTerritory.class);
releaseColl.setInternalClass("ReleaseDetailsByTerritory.2", ReleaseDetailsByTerritory.class);
You should use Morphia. It adds support for POJOs and embedded objects (and collections). It doesn't have any of the limitations the driver does about requiring your classes to look like a Map<String, Object>.

Categories