I have a method that takes a list of entities (Classes) and does some computation. To describe my needs here is the simplest outline of the method (pseudo-code):
public void do(List<Class<?> entities) {
for (Class<?> entity : entities) {
List<?> list = session.createCriteria(entity).list();
for (Object o : list) {
System.out.println(o.getClass().getSimpleName() + " " + o.getId());
}
}
}
Is there a way that I can access/get the Id of o?
session.getIdentifier(o)
Well, maybe it'll get many critics but all your entity classes may implement this interface:
public interface EntityWithId {
Integer getId();
void setId(Integer id);
}
If your id's are not integers may be the interface could be:
public interface EntityWithId<T> {
T getId();
setId(T id);
}
public class Entity implements EntityWithId<String> {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
.
.
.
}
Anyway, I don't see why you'd want to get the IDs of all your entities, it's a strange solution for a strange requirement, hehe.
Not unless you have the property named the same on all entities, then you could use reflection to call the getter for that property. If they are all named something different, then you would get a NoSuchMethodException
Related
I have classes similar to DataRequest & DataWithIdRequest. DataWithIdRequest gets passed into my controller method. I want to pass the subclass object ONLY to another class for processing. However, when I try to downcast to DataRequest the extra field is still showing. How can I accomplish this?
public class DataRequest {
private String name;
public String getName() {
return name;
}
public void setFirstName(String name) {
this.name = name;
}
}
public class DataWithIdRequest extends DataRequest {
private Integer id;
public Integer getId() {
return contractKey;
}
public void setContractKey(Integer id) {
this.id = id;
}
}
//controller
processData(request);
}
//domain class
public Boolean processData(DataRequest request) {
//request here has DataWithIdRequest field
//but I only want the subclass
}
public Boolean processData(DataRequest request) { }
Because in your method processData, request's type is DataRequest. You want it to be DataWithIdRequest.
public Boolean processData(DataWithIdRequestrequest request) { }
You can only offer a part of an API (application programmer's interface), by separating the code in an interface.
public interface Identified {
Integer getId();
public void setContractKey(Integer id);
}
public class DataWithIdRequest extends DataRequest implements Identified {
private Integer id;
#Override
public Integer getId() {
return contractKey;
}
#Override
public void setContractKey(Integer id) {
this.id = id;
}
}
public Boolean processData(DataRequest request) {
if (request instanceOf Identified identified) {
identified.setContractKey(13);
}
}
Or move the problem to the caller:
public Boolean processData(Identified request) {
request.setContractKey(13);
}
By the way it more usual to use int, boolean, the primitive types.
To hide the information completely, you need to create a new instance of DataRequest by DataWithIdRequest (that's why mapping library like mapstruct is useful), but not directly passing it.
Explanation:
This is how inheritance works, imagine a method takes a parameter of an interface or abstract class, by using instanceof inside the method we can check the object actual type and do something specific. e.g.
public void drawShape(Shape shape) {
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
// do sth
} else if (shape instanceof Square) {
// do sth else
}
}
The above example is completely valid (although not a good programming style, that's another story).
The object inside the memory holds all the actual class details, passing it to a method doesn't change anything to the memory. The parameter (e.g. shape) is only another reference to the same memory location.
Maybe I don't understand the question fully but: as you want I it's not possible since:
public class DataWithIdRequest extends DataRequest
means DataRequest is a subset of DataWithIdRequest, it's an intersection.
You unfortunately need to find an other way
I have a Java database application using hibernate, with different Classes that have the same attributes (here: “active”).
In an interface, there is a function that retrieves entries from a database based on such an attribute (active).
So far, I was doing this:
//interface
public interface ObjSvcIntf {
default <Entity> ArrayList<Entity> get(Boolean active);
}
//implementation 1
public class ObjCarSvc implements ObjSvcIntf {
#SuppressWarnings("unchecked")
#Override
public ArrayList< ObjCar > get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + ObjCar.class.getSimpleName() + " where active = :active");
query.setParameter("active", active);
if (!query.list().isEmpty()) {
return (ArrayList< ObjCar >) query.list();
} else {
return null;
}
}
//implementation 1
public class ObjPersonSvc implements ObjSvcIntf {
#SuppressWarnings("unchecked")
#Override
public ArrayList< ObjPerson > get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + ObjPerson.class.getSimpleName() + " where active = :active");
query.setParameter("active ", active);
if (!query.list().isEmpty()) {
return (ArrayList< ObjPerson >) query.list();
} else {
return null;
}
}
As You can see, there is a lot of redundant code in each implementing class, which I would like to avoid.
What I would like instead therefore, is to have a generic default function in the interface, which will return the same for each implementation of the interface (unless overridden by the implementing class of course).
I.e., something like this (except this does not work, of course):
public interface ObjSvcIntf {
default <Entity> ArrayList<Entity> get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + Entity.class.getSimpleName() + " where active = :active");
query.setParameter("active", active);
return (ArrayList<Entity>) query.list();
}
}
I am lacking the proper understanding here, how to create the function in the interface in the right way, to be able to use it in different contexts/ different classes.
How can I adjust the function in the interface instead to make this happen?
public interface ObjSvcIntf<Entity> {
default <Entity> ArrayList<Entity> get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + getImplClass().getSimpleName() + " where active = :active");
query.setParameter("active", active);
return (ArrayList<Entity>) query.list();
}
Class getImplClass();
}
And you could just provide that same class for each implementation as you provide for the generic type.
You can create an abstract function to return the object of the subclass. Something like this would work. Here is example code, where interface returns as list of object of the class implementing the interface.
public interface ObjSvcIntf<E> {
default List<E> get(Boolean active) {
var list = new ArrayList<E>();
list.add(self());
return list;
}
E self(); // function to return the sub class instance
}
I restructured the project to separate the Interface from its implementation.
Each class, extending the (abstract) implementation of the interface now sets an attribute of Type "Class" when calling the super constructor, with each function in the abstract class referring to that attribute.
Is there a better way?
What are potential problems with this approach?
Interface:
public interfaceObjSvcIntf {
<Entity> Object getById(Long id);
}
Implementing abstract Class:
public abstract class ObjSvcImpl implements ObjSvcIntf {
public Class<?> servicedClass;
// CONSTRUCTOR
public ObjSvcImpl(Class<?> servicedClass) {
this.servicedClass = servicedClass;
}
#Override
public <Entity> Object getById(Long id) {
return DB.getById(this.servicedClass, id);
}
}
Service Class:
public class ObjCarSvc extends ObjSvcImpl {
public ObjCarSvc() {
super(ObjCar.class);
}
}
Model Class:
#Entity
#Table(name = "OBJ_CAR")
public class ObjCar implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "OBJ_CAR_ID")
private Long objCarId;
#NotNull
#Column(name = "NAME")
private String name;
// Getters and Setters
}
I'm lazy person... i just want the simplest way, now i'm learning android room. This might be answered here "Why is it not possible to extend annotations in Java?", but it seems like to try pass annotation to another annotation, right? So, Could i pass annotation to the child class for example, here is my parent class 'Model':
#Entity
public abstract class Model implements Parcelable{
#Nullable
#PrimaryKey
private Object id;
public String toJSON(){
return new Gson().toJson(this);
}
public <T> T getId(Class<T>type) {
if(type.equals(Integer.class)||type.equals(int.class)) {
return (T) Integer.valueOf(new Double(Double.parseDouble(String.valueOf(id))).intValue());
}
else if(type.equals(Long.class)||type.equals(long.class)) {
return (T) Long.valueOf(new Double(Double.parseDouble(String.valueOf(id))).longValue());
}
else if(type.equals(Double.class)||type.equals(double.class)) {
return (T) Double.valueOf(id.toString());
}else if(type.equals(UUID.class)){
return (T) UUID.fromString(String.valueOf(id));
}
return type.cast(id);
}
public <T> void setId(T id) {
this.id = id;
}
}
I tried to pass #Entity to child class.
public class Movie extends Model {
public static final String IDENTIFIER = Movie.class.getSimpleName();
#SerializedName("popularity")
private double popularity;
#SerializedName("vote_count")
private long voteCount;
#SerializedName("video")
private boolean video;
}
Because my data access object cannot detect my 'Movie' class, except i'm added Entity annotation
#Dao
public interface FavMovieDAO {
#Query("SELECT * FROM Movie WHERE id = :movieId")
public Movie find (int movieId);
}
If there is no way, i'll declare my entire model class as an Entity... :/. Thanks in advance.
I'm evaluating Spring Data's support for Couchbase, and have have run across the following issue. Consider the following pseudo-code example where I've two POJO classes, and a repository defined and instantiated for each:
public class Foo
{
#Id
private String _id;
#Version
private Long _rev;
// .. Other data and members.
}
public class Bar
{
#Id
private String _id;
#Version
private Long _rev;
// .. Other data and members.
}
//
// Repositories
//
#Repository
public interface FooRepository extends CrudRepository<Foo, String> {
}
#Repository
public interface BarRepository extends CrudRepository<Bar, String> {
}
Both repositories are utilizing the same Couchbase bucket. Next:
// Create a new Foo object and save it.
Foo f = new Foo( "id_1" );
fooRepository.save( f );
// Now, try fetching a Bar object using the ID of a Foo object (?)
Bar b = barRepository.findOne( "id_1" );
This results in a Bar object being returned, but not properly initialized - no exceptions are raised. The question, is why isn't an error indicated in this scenario? It seems like not much of a stretch to raise an exception when the requested type doesn't match the persisted type. Am I missing something?
FWIW, When I look at the raw documents in Couchbase via the admin console, I observe that each contains a "_class" property with presumably could be used to identify classes used to represent the data, and detect such mis-matches.
The problem here is that a document (JSON serialized entity) is stored associated to the id (#Id field) on same bucket as other entities, this generates a kind of ambiguity, saving Entity1 with id 1, will overwrite Entity2 with id 1.
An application side solution would be to store entities with distinct keys, in the case of Entity1 with id 1 something like: 1-Entity1 as key.
Maybe you could solve the problem with an approach similar to this one:
public Entity1 {
private static final String ENTITY_NAME = "ENTITY1";
#Id
private String key;
public void setKey(String key) {
this.key = key;
}
public String getKey(String key) {
return this.key;
}
public void setId(String id) {
this.key = ENTITY_NAME + ":" + id;
}
public String getId() {
if (null == this.key)
return null;
return this.key.substring(ENTITY_NAME.length()+1);
}
}
I know it's a bad idea, and it causes bugs. The problem is, I need the "intended" behavior.
"Low":
// simplified example
abstract class Low {
String name;
public Low(String name) {
this.name = name;
}
public Low(int id) {
this.name = getNameForId(id);
}
public Low() {} // will be loaded later
#Override
public String toString()
{
return name;
}
public void load(InputStream in) {
// --- grab ID from stream ---
this.name = getNameForId(id);
}
protected abstract String getNameForId(int id);
}
And "High":
class High extends Low {
public High(int id) { super(id); }
public High(String name) { super(name); }
public High() {} // will be loaded later
#Override
protected String getNameForId(int id)
{
return Registry.getName(id);
}
}
Note that in this particular case, it will work just fine. But it will fall apart once the overriding method needs to use some field.
How to do this better?
You want to separate out loading a name for an ID from your Low and High objects. Introduce an interface for loading names given an ID.
public interface NameProvider
{
String getNameForId(String id);
}
Add specific implementations for each source of names.
public class InputStreamNameProvider implements NameProvider
{
private InputStream inputStream;
// Constructor
public String getNameForId(String id)
{
// return name loaded via inputStream
}
}
public class RegistryNameProvider implements NameProvider
{
public String getNameForId(String id)
{
return Registry.getName(id);
}
}
You could then add a new constructor to Low that takes the NameProvider and String id as arguments
public Low(NameProvider provider, String id)
{
this(provider.getNameForId(id));
}
or even use the name provider before constructing the instance of Low or High. The main idea is the separation of loading the name for an ID from the Low and High objects.
You can avoid calling the abstract method by adding a public void load(int id) method (like you do for InputStream) and removing the Constructor(int id).
You might want to add some Factory functionality to ensure your constructed instance is can never be accessed without having a proper name value.