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
}
Related
How do i add a custom query to my Spring boot application and access it in the controller?
I have two tables called CarBrand and YearMade. CarBrand has ID, code and Brand as columns. YearMade also has ID, code and year as columns.
I have written my model classes with setter and getter methods for each entity. I have added my repository interfaces and my service classes.
public interface YearRepository extends JpaRepository<Year, Long> {
}
My Brand Repository
public interface BrandRepository extends JpaRepository<Brand, Long> {
#Query("select b from brand b where brand.brand = ?1")
List<Brand> findVehicleBrand(String brand);
}
Here is my service class
public class YearService {
#Autowired
private YearRepository yearRepository;
public List<Year> listAll(){
return yearRepository.findAll();
}
public void save(Year engineSize){
yearRepository.save(engineSize);
}
public Year get (long id){
return yearRepository.findById(id).get();
}
public void delete (Long id){
yearRepository.deleteById(id);
}
}
My Brand Service
public interface BService {
List<Brand> findVehicleBrand(String name);
}
And this.
#Service
#Transactional
public class BrandService implements BService{
#Autowired
private BrandRepository brandRepository;
public List<Brand> listAll(){
return brandRepository.findAll();
}
public void save(Brand brand){
brandRepository.save(brand);
}
public Brand get (long id){
return brandRepository.findById(id).get();
}
public void delete (Long id){
brandRepository.deleteById(id);
}
#Override
public List<Brand> findVehicleBrand(String name) {
var brand = (List<Brand>) brandRepository.findVehicleBrand(name);
return brand;
}
}
In my controller, I get a path variable with a string, i use substring to break the string into two. The two substrings have the code for brand and year. The first two represent the year and the other three represent the brand. How do i compare the codes to the codes in the database to get the actual year and brand.
http://localhost:8081/vincode/wwQPT
The ww is the code for the year 1990 and QPT is for Honda Motor Company in the database.
I want a JSON response like this
{
Year Made : 1990,
Brand Name : Honda Motor Company
}
Here is the controller class i have so far.
#RequestMapping("/{vincode}")
public #ResponseBody String getAttr(#PathVariable(value="vincode") String vincode) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
System.out.println(yr);
return yr;
}
Where do i add the query and how do i use it in my controller?
Thank you.
If you dont have request mappong wlth value top of the class then http://localhost:8081/vincode/ww/QPT
RequestMapping("/vincode/{code}/{company}") can be more useful
There is no need to use substring maybe code or company key sizes changes.
Also service layer can be injected and used anytime.
Firstly add this statement in BrandRepository interface :
public interface BrandRepository extends JpaRepository<Brand, Long> {
#Query("select b from brand b where brand.brand = ?1")
List<Brand> findVehicleBrand(String brand);
public Brand findByCode(String code);
}
In YearRepository interface :
public interface YearRepository extends JpaRepository<Year, Long> {
public Year findByCode(String code);
}
Then add this method in BrandService Class:
public String findByCode (String code){
return brandRepository.findByCode(code).getBrand();
}
Then add this method in YearService Class:
public String findByCode (String code){
return yearRepository.findByCode(code).getYear;
}
Create Domain Class :
public class YearBrand
{
private String YearMade;
private String BrandName;
public YearBrand(String year, String brand)
{
this.YearMade=year;
this.BrandName=brand;
}
}
Then In Controller Class :
#RequestMapping("/{vincode}")
public YearBrand getAttr(#PathVariable(value="vincode") String vincode) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
return new YearBrand(yearService.findByCode(yr),brandService.findByCode(brand));
}
NOTES:
Make sure your Controller Class is annotated with #RestController
Spring Data JPA derives queries based on method naming conventions.
So, to get year by code in YearMade table, you need to modify your YearReporsitory interface like this (add an abstract method):
public interface YearRepository extends JpaRepository<Year, Long> {
// set return type as required
//find - Do What, ByCode - Criteria.
public Integer findByCode(String code);
}
And, use this method in your YearService just as you've used other methods.
But, you cannot use the same method for getting brand by code requirement. You'll have to write a repo class for it like:
public interface BrandRepository extends JpaRepository<CarBrand, Long> {
public Integer findByCode(String code);
}
You can write these methods for all the members of your Entity class. You've to follow the naming convention to get Spring recognize it.
EDIT (to show how to use this in controller and service class):
YearRepository interface:
public interface YearRepository extends JpaRepository<Year, Long> {
// set return type as required
//find - Do What, ByCode - Criteria.
public Integer findByCode(String code);
}
BrandRepository
public interface BrandRepository extends JpaRepository<Brand, Long> {
/*The below two methods are abstract methods.*/
// it must follow the findby<MemberName> convention
//return CarBrand
CarBrand findByBrand(String brand);
/*return a CarBrand Entity*/
public CarBrand findByCode(String code);
YearService:
public class YearService {
#Autowired
private YearRepository yearRepository;
public List<Year> listAll() {
return yearRepository.findAll();
}
public void save(Year engineSize) {
yearRepository.save(engineSize);
}
public Year get(long id) {
return yearRepository.findById(id).get();
}
public void delete(Long id) {
yearRepository.deleteById(id);
}
public int getYearByCode(String code) {
//here, we're using this method just as you've used the methods above.
//Spring constructs the query at runtime
return yearRepository.findByCode(code); //<-- usage of the custom method
}
}
BService:
public interface BService {
CarBrand findVehicleBrand(String name);
}
BrandService:
#Service
#Transactional
public class BrandService implements BService{
#Autowired
private BrandRepository brandRepository;
public List<Brand> listAll(){
return brandRepository.findAll();
}
public void save(Brand brand){
brandRepository.save(brand);
}
public Brand get (long id){
return brandRepository.findById(id).get();
}
public void delete (Long id){
brandRepository.deleteById(id);
}
#Override
public CarBrand findVehicleBrand(String name) {
//var brand = (List<Brand>) brandRepository.findVehicleBrand(name);
var brand = brandRepository.findByBrand(name); //<-- using the custom method in brandRepository
return brand;
}
}
Your RepsonseDto:
class RepsonseDto {
private String yearMade;
private brandName;
//getters and setters
/*Use #JsonProperty("Year Made") and #JsonProperty("Brand Name") on your getters. Otherwise, you will get json reposnse as: "yearMade" and "brandName"*/
}
Controller:
There are better ways to write controllers and inject dependencies. Let's keep it simple for now.
#RequestController
class YourController {
//inject dependencies
#Autowired
YearService yearService;
#Autowired
BrandService brandService;
#RequestMapping("/{vincode}")
// the definition for ResponseEntity is above
public ResponseEntity<RepsonseDto> getAttr(#PathVariable(value="vincode") String vincode) {
// create a ReponseEntity object
RepsonseDto retEntity = new RepsonseDto();
// do a check for null and expected length of vincode
if(vincode != null && vincode.length() == 5) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
retEntity.setYearMade(yearService.getYearByCode(yr));
retEntity.setBrandName(brandService.findVehicleBrand(brand));
System.out.println(yr);
}
return new ResponseEntity<>(retEntity, HttpStatus.OK)
}
NOTE: I didn't use an IDE to write this. There may be compiler errors. Hope this gives you an idea of it all fits in.
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 would like to make a generic method to get a List from the parameter object.
The problem is because I have a declared object with a instance of the other class that extends the declared class.
I don't want to use the instanceof solution because the number of classes that extends LimitedValue can be big.
I thought to use reflection for a solution, but I don't know how to use that with an instance of object, in this part of the code:
Class cls = Class.forName(limitedValue.getClass().getName());
Object obj = cls.newInstance();
//This is wrong, I don't want a new instance.
Method[] methods = cls.getDeclaredMethods();
for(int x= 0; x < methods.length; x++) {
Method method = methods[x];
if ("java.util.List".equals(method.getReturnType().getName())) {
//How to get the value of this method from limitedValue instance ?
}
}
This is my full code:
public class CalculatorLimitedValue {
public static void main(String[] args) throws Exception {
StoreItem storeItem = new StoreItem(1L, "Name of StoreItem", 50L);
List listOfStoreItems = new ArrayList();
listOfStoreItems.add(storeItem);
LimitedValue limitedValue0 = new Store(listOfStoreItems);
List firstList = calculator(limitedValue0);
//do something with the list
SupermarketItem supermarketItem = new SupermarketItem(1L, "Name of SupermarketItem", 21L);
List listOfSupermarketItems = new ArrayList();
listOfSupermarketItems.add(supermarketItem);
LimitedValue limitedValue1 = new Supermarket(listOfSupermarketItems);
List secondList = calculator(limitedValue1);
//do something with the list
}
/** This is the method that I'd like to make generic to return a List */
private static List calculator(LimitedValue limitedValue) throws Exception{
Class cls = Class.forName(limitedValue.getClass().getName());
Object obj = cls.newInstance();
//This is wrong, I don't want a new instance.
Method[] methods = cls.getDeclaredMethods();
for(int x= 0; x < methods.length; x++) {
Method method = methods[x];
if ("java.util.List".equals(method.getReturnType().getName())) {
//How to get the value of this method from limitedValue instance ?
}
}
/* I don't want to use this one way, because my classes that extends LimitedValue
can be big. I would like to made a generic way to get de list of classes. */
if (limitedValue instanceof Store) {
System.out.println("This is a store");
return ((Store) limitedValue).getStoreItems();
} else if (limitedValue instanceof Supermarket) {
System.out.println("This is a supermarket");
return ((Supermarket) limitedValue).getSupermarketItems();
}
return null;
}
}
If it help, these are my other classes:
LimitedValue.class
public class LimitedValue { }
StoreItem.class
public class StoreItem {
private Long id;
private String nameOfStoreItem;
private Long valueOfStoreItem;
public StoreItem(Long id, String nameOfStoreItem, Long valueOfStoreItem){
this.id = id;
this.nameOfStoreItem = nameOfStoreItem;
this.valueOfStoreItem = valueOfStoreItem;
}
//getters and setters...
}
SupermarketItem.class
public class SupermarketItem {
private Long id;
private String nameOfSupermarketItem;
private Long valueOfSupermarketItem;
public SupermarketItem() {
}
public SupermarketItem(Long id, String nameOfSupermarketItem, Long valueOfSupermarketItem) {
this.id = id;
this.nameOfSupermarketItem = nameOfSupermarketItem;
this.valueOfSupermarketItem = valueOfSupermarketItem;
}
//getters and setters...
}
Store.class
public class Store extends LimitedValue {
private List<StoreItem> storeItems;
public Store(List<StoreItem> storeItems) {
this.storeItems = storeItems;
}
//getters and setters
}
Supermarket.class
public class Supermarket extends LimitedValue {
private List<SupermarketItem> supermarketItems;
public Supermarket(List<SupermarketItem> supermarketItems) {
this.supermarketItems = supermarketItems;
}
//getters and setters
}
You could try to use reflection here to try to achieve what you want, but it would be better to reconsider your overall design and try to use a better object oriented design that solves the problem at hand.
In particular, lets say we consider adding a method called getItems to the LimitedValue class that returns a List of items, which may be SupermarketItems or may be StoreItems. If it is structured correctly, you won't need to know the actual type because the code will be abstracted over it polymorphically.
public abstract class LimitedValue {
List<? extends Item> getItems();
}
We've now defined a new method on LimitedValue, but we also have to consider that we've introduced this new Item thing. I note that the SupermarketItem and StoreItem all share similiar attributes, name, id and value, so it seems that it might be possible to use a single class to represent them all.
public abstract class Item {
final Long id;
final String name;
final Long value;
public Item(final Long id, final Long name, final Long value) {
this.id = id;
this.name = name;
this.value = value;
}
String getName() {
return name;
}
// other getters and setters
}
public class SupermarketItem extends Item {
public SupermarketItem(final Long id, final Long name, final Long value) {
super(id, name, value);
}
}
public class StoreItem extends Item {
public StoreItem(final Long id, final Long name, final Long value) {
super(id, name, value);
}
}
Now we've completely abstracted away the need for any reflection when accessing these objects - you can simply call item.getValue() as you will know that every item in the list is of type Item.
Of course, you'll also need to refactor the Store and SuperMarket classes, for example:
public class Supermarket extends LimitedValue {
private List<SupermarketItem> supermarketItems;
public Supermarket(List<SupermarketItem> supermarketItems) {
this.supermarketItems = supermarketItems;
}
public List<? extends Item> getItems() {
return supermarketItems;
}
}
and because you are only returning a List<Item> you always know what is in it, and you can change your main code to work with this.
This is a much cleaner long term solution.
To get the List value, use Method#invoke:
List list = method.invoke(limitedValue);
You don't need Object obj = cls.newInstance(); - you're not using it at all in the method.
In any case, you're making it very difficult for yourself. You could also define an interface
public interface HasList<E> {
List<E> getList();
}
and have all classes implement this.
For instance i have some entity - Product
public class Product {
...
private String name;
private int count;
private Product associatedProduct;
...
// GETTERS & SETTERS
}
And also i have product finder which allows to find the product by filters:
public interface Finder<T> {
Set<T> find(Filter... filters);
}
And now i can execute the following code:
Finder<Product> finder = ...;
// find all products with name 'cucumber'
Set<Product> finder.find(Filter.equals("name", "cucumber"));
We don't like this code because i should have the 'soft' link to field name "name" and i can't have compile time exception in case misprint or any other mistake.
For this reason i have created the code generator which generate static links to properties.
The generated class looks like:
public final class $Product {
private final String context;
// some factory is used to instance creation
$PostEntity() {this.context = "";}
$PostEntity(String context) {this.context = context;}
public String name() { return context + "name";}
public String count() { return context + "count";}
public String associatedProduct() { return context + "associatedProduct";}
public $Product associatedProductDot() { return new $Product( this.context + "associatedProduct.");}
}
For now i can make the following:
Set<Product> finder.find(Filter.equals(Links.PRODUCT.name() , "cucumber"));
//or
Set<Product> finder.find(Filter.equals(Links.PRODUCT.associatedProductDot().name() , "cucumber"));
It works like a charm and i happy.
I know alternative approach with using proxy objects, but it imposes additional overhead in runtime and adds some magical moment in code, so this variant does not suit me.
And finally my question:
There is a more elegant approach to implement this functionality with using java 8?
Java 8 has everything you need:
public static <C,P> Predicate<C> byProperty(Function<C,P> f, P value) {
return component->Objects.equals(f.apply(component), value);
}
public static <C> Set<C> find(Collection<? extends C> c, Predicate<? super C> p) {
return c.stream().filter(p).collect(Collectors.<C>toSet());
}
The standard interface for filtering is called Predicate and the first method above allows you to create arbitrary Predicates for matching a property of a component type C. The second method shows how you can get a Set of matching components out of a Collection using the Stream API. Then you can use it like this:
List<Product> list;
…
Set<Product> set=find(list, byProperty(Product::getName, "foo"));
or
Set<Product> set=find(list, byProperty(Product::getCount, 42));
Note that this is type safe and contains compile-time checked references (your “hard links”) to your properties. The only difference to what you have asked for is that they refer to the getter method rather than to the field names, as a) field references are not supported and b) your fields are private anyway.
Note that you can augment these methods by another factory allowing to provide a value-predicate rather than a constant:
public static <C,P> Predicate<C> matchProp(
Function<C,P> f, Predicate<? super P> value) {
return component->value.test(f.apply(component));
}
This allows use cases like:
Set<Product> set=find(list, matchProp(Product::getCount, count -> count>100));
See Lambda Expressions
or
Set<Product> set=find(list, matchProp(Product::getName, String::isEmpty));
The fastest thing is to provide your own implementation of the Filter interface. Since I don't know your Filter interface, I have to make an assumption about how it looks like. Here is my assumption:
public interface Filter<T> {
boolean matches(T t);
}
By the way, I think the interface Finder should instead look like this:
public interface Finder<T> {
Set<T> find(Filter<? super T>... filters);
}
So, you could have a class like this:
public final class ProductFilters {
private ProductFilters() { /* Utility class */ }
public static Filter<Product> byName(final String name) {
return new Filter() {
public boolean matches(Product t) {
return name.equals(t.getName());
}
}
}
}
You could even put this inside class Product, which can make it a little bit nicer:
public class Product {
private String name;
public static final class Filters {
private Filters() { /* Utility Class */ }
public static Filter<Product> byName(final String name) {
return new Filter() {
public boolean matches(final Product t) {
return name.equals(t.name);
}
};
}
}
}
And yes, Java 8 makes this stuff nicer, the explicit anonymous class can syntactically be replaced by a lambda, like this:
public class Product {
private String name;
public static final class Filters {
private Filters() { /* Utility Class */ }
public static Filter<Product> byName(final String name) {
return t -> name.equals(t.name);
}
}
}
Your code that uses the filters could now look like this:
Set<Product> cucumbers = finder.find(Product.Filters.byName("cucumber"));
The Filter<T> interface is present in Java 8 in package java.util.function. It's name there is Predicate<T>, and the essential part looks like this:
public interface Predicate<T> {
boolean test(T t);
}
If the products that are to be filtered can be made available as Stream either directly, or via a Collection, you can use the new java.util.stream API for filtering. For the example I assume that the products to be filtered are in a Set, too. The code that filters products could look like this:
Set<Product> potentialCucumbers = ...;
// Inline lambda:
Set<Product> cucumbers = potentialCucumbers.stream().filter(p -> "cucumber".equals(p.getName())).collect(Collectors.toSet());
// Stored lambda as above:
Set<Product> cucumbers = potentialCucumbers.stream().filter(Product.Filters.byName("cucumber")).collect(Collectors.toSet());
I really like static imports for that stuff as they can significantly reduce line length. With static imports it looks like this:
Set<Product> potentialCucumbers = ...;
// Inline lambda:
Set<Product> cucumbers = potentialCucumbers.stream().filter(p -> "cucumber".equals(p.getName())).collect(toSet());
// Stored lambda as above:
Set<Product> cucumbers = potentialCucumbers.stream().filter(byName("cucumber")).collect(toSet());
My suggestion would be to use predicates rather than your Filter classes. They make for cleaner code. I would also suggest making commonly used properties like "name" or "owner" into interfaces that provide predicates for searchability. For instance, for the "name" and "owner" properties you might have two interfaces called "Named" and "Owned":
public interface Named {
public String getName();
public void setName(String name);
static <T extends Named> Predicate<T> nameEquals(Class<T> clazz, String s){
return ((p) -> {
if (s == null){
return p.getName() == null;
}
return s.equals(p.getName());
});
}
}
public interface Owned {
public String getOwner();
public void setOwner(String owner);
public static <T extends Owned> Predicate<T> ownerEquals(Class<T> clazz, String s){
return ((p) -> {
if (s == null){
return p.getOwner() == null;
}
return s.equals(p.getOwner());
});
}
}
Then your Product class implements these interfaces, along with a couple simple convenience methods for calling the interface static methods:
public class Product implements Named, Owned{
private String name;
private String owner;
public String getOwner() {
return owner;
}
public String getName() {
return name;
}
public void setOwner(String owner){
this.owner = owner;
}
public void setName(String name){
this.name = name;
}
public static Predicate<Product> nameEquals(String s){
return Named.nameEquals(Product.class, s);
}
public static Predicate<Product> ownerEquals(String s){
return Owned.ownerEquals(Product.class, s);
}
}
And voila, your Product is searchable. Then your find() method's signature changes to take a predicate:
public interface Finder<T> {
Set<T> find(Predicate p);
}
One of the wonderful things about predicates is how easy they are to combine and compound with one another. For example, let's say we want to find() any products named "cucumber" who aren't owned by "john", or any products owned by "john" with any other names. The call to find() is pretty clean and understandable:
finder.find(
Product.nameEquals("cucumber")
.and(Product.ownerEquals("john").negate())
.or(
Product.ownerEquals("john")
.and(Product.nameEquals("cucumber").negate())
)
);
I should be pretty clear what this block of code is doing. I used the indentation to try and make it clearer how they combine. We can combine the different predicates to our hearts' content.
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