Dynamic search by entites with inheritance - spring

In my service I created abstract class ShapeEntity with two abstract methods.
#Entity
#Inheritance(strategy = TABLE_PER_CLASS)
public abstract class ShapeEntity {
#Id
private String id = UUID.randomUUID().toString();
public abstract double getArea();
public abstract double getPerimeter();
}
Other entites inherit id and methods, but have own parameters like for example radius or width:
#Entity
#Table(name = "circles")
public class CircleEntity extends ShapeEntity {
private Double radius;
#Override
public double getArea() {
return Math.PI * Math.pow(2, radius);
}
#Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
#Entity
#Table(name = "squares")
public class SquareEntity extends ShapeEntity {
private double width;
#Override
public double getArea() {
return width * width;
}
#Override
public double getPerimeter() {
return 4 * width;
}
}
and now I am working at get all objects with some parameters.
I would like to make something like findAll() with parameters for example area, type, and radius, width.
I tried with:
tkaczmarzyk
/
specification-arg-resolver but when I put radius as request parameter I am getting error:
"Unable to locate Attribute with the the given name [radius] on this ManagedType [com.example.ShapeEntity]; nested exception is java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [radius] on this ManagedType [com.example.ShapeEntity]" becouse it can not find field in ShapeEntity (what is obvious).
So is there any other way to make dynamic earch by request parameters? Can I do it by one findAll() method with some parameters?
I can do it with many if / else loops but I think it will not be the best solution.
The second problem is what if I add rectangle (RectangleEntity) which has width too.. I would like to inlcude every shape which has given width so searching should be through squares and rectangles.

Related

AspectJ member introduction doesn't build

I have a POJO:
class Test {
private int i;
void setI(int i) {
this.i = i;
}
}
this is what I have so far for the aspect:
public aspect t perthis(within(#Tracking *)){
private Set<String> set = new HashSet<>();
pointcut setterMethod() : execution(public void set*(..));
after(Object o) returning() : setterMethod() && this(o) {
set.add(thisJoinPoint.getSignature().getName());
System.out.println(set);
}
public Set<String> go() {
return set;
}
}
I want a Set<String> set for any instance of ANY class that has #Tracking. I also want to add the go() method for any instance of ANY class that has #Tracking.
Can't figure out the syntax. The go() method doesn't get added. If I put Test.go() then the method get added, but then it crashes during runtime.
Marker interface:
package de.scrum_master.tracking;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(TYPE)
public #interface Track {}
Sample POJOs with/without marker annotation:
We use two sample classes for positive/negative tests.
package de.scrum_master.app;
import de.scrum_master.tracking.Track;
#Track
class TestTracked {
private int number;
private String text;
public void setNumber(int number) {
this.number = number;
}
public void setText(String text) {
this.text = text;
}
#Override
public String toString() {
return "TestTracked [number=" + number + ", text=" + text + "]";
}
}
package de.scrum_master.app;
class TestUntracked {
private int number;
private String text;
public void setNumber(int number) {
this.number = number;
}
public void setText(String text) {
this.text = text;
}
#Override
public String toString() {
return "TestUntracked [number=" + number + ", text=" + text + "]";
}
}
"Dirty tracker" interface:
We want the aspect to implement the following interface for each class annotated by #Track by means of inter-type declaration (ITD).
package de.scrum_master.tracking;
import java.util.Set;
public interface Trackable {
Set<String> getDirty();
}
Driver application:
Here, we are assuming that each POJO class annotated with the marker interface automagically implements the Trackable interface and therefore knows the getDirty() method, which the we call in order to verify that the aspect correctly tracks setter calls.
package de.scrum_master.app;
import de.scrum_master.tracking.Trackable;
public class Application {
public static void main(String[] args) {
TestTracked testTracked = new TestTracked();
testTracked.setNumber(11);
testTracked.setText("foo");
System.out.println(testTracked);
if (testTracked instanceof Trackable)
System.out.println("Dirty members: " + ((Trackable) testTracked).getDirty());
TestUntracked testUntracked = new TestUntracked();
testUntracked.setNumber(22);
testUntracked.setText("bar");
System.out.println(testUntracked);
if (testUntracked instanceof Trackable)
System.out.println("Dirty members: " + ((Trackable) testUntracked).getDirty());
}
}
Aspect:
This aspect makes each #Track-annotated class implement interface Trackable and provides both a private field storing tracking information and a getDirty() method implementation returning its value. Furthermore, the aspect makes sure to store the "dirty" information for each successfully executed setter.
package de.scrum_master.tracking;
import java.util.HashSet;
import java.util.Set;
public aspect TrackingAspect {
private Set<String> Trackable.dirty = new HashSet<>();
public Set<String> Trackable.getDirty() {
return dirty;
}
declare parents : #Track * implements Trackable;
pointcut setterMethod() : execution(public void set*(..));
after(Trackable trackable) returning() : setterMethod() && this(trackable) {
System.out.println(thisJoinPoint);
trackable.dirty.add(thisJoinPoint.getSignature().getName().substring(3));
}
}
Console log:
You will see this when running the driver application:
execution(void de.scrum_master.app.TestTracked.setNumber(int))
execution(void de.scrum_master.app.TestTracked.setText(String))
TestTracked [number=11, text=foo]
Dirty members: [Number, Text]
TestUntracked [number=22, text=bar]
The reason why we do not need perthis or pertarget instantiation is that we store the "dirty" information right inside the original classes. Alternatively, we could use per* instantiation and keep all information inside the corresponding aspect instances instead of using ITD. In that case however, the "dirty" information would be unaccessible from outside the aspect, which might even be desirable with regard to encapsulation. But then, whatever action needs to be performed when storing the "dirty" instances, would also need to happen from inside the aspect. As you did not provide an MCVE and hence your question is lacking detail, I did not consider this functionality in the aspect. I can easily imagine how this could be done with both aspect variants - per* instantiation vs. ITD - but I hate to speculate and then be wrong.

Error when trying to fetch changes for class extending HashMap

I'm trying a simple test where I try to commit an object of a Class that extends HashMap. I'm left with a MANAGED_CLASS_MAPPING_ERROR: given javaClass 'class com.vehco.Configuration' is mapped to MapType, expected ManagedType. Do Javers not support is-a but only has-a?
Been reading the documentation forwards and backwards but unable to find anything. Google was neither my friend this time around.
Please find the test code below:
Tester.java:
public class Tester {
Javers javers = JaversBuilder.javers().build();
public static void main(String[] args) {
Tester t = new Tester();
t.start();
}
private void start() {
Configuration data = new Configuration("global");
for (Integer i = 0; i < 10; i++) {
data.getProp().put(i.toString(), UUID.randomUUID().toString());
}
javers.commit("svenie",data);
for (Integer i = 0; i < 10; i++) {
if (i % 2 == 0)
data.getProp().put(i.toString(), UUID.randomUUID().toString());
}
javers.commit("svenie",data);
List<Shadow<Configuration>> changes = javers.findShadows(QueryBuilder.byClass(Configuration.class).build());
for (Shadow<Configuration> change : changes) {
System.out.println(change.getCommitMetadata());
}
}
}
Configuration.java:
public class Configuration extends HashMap<String,String> {
#Id
private String name;
private Properties prop = new Properties();
public Configuration(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Properties getProp() {
return prop;
}
}
Looks like the Javers type inferring is ambiguous here. On the one hand your class extends Map so JaVers can infer it as MapType, on the other hand it has #Id property, so JaVers can infer it as Entity. I think you have to decide which Javers type is better for your class and register this class explicitly using JaversBuilder.register*()?
A class can't be mapped to more than one Javers type.

Projection create new field

I have a class that represents a user date of birth in two separated fields
public class User {
private int yearOfBirth;
private int monthOfBirth;
}
Is it possible to make a projection that exports the user age? I know we can concatenate fields using #Value.
The easiest way to resolve the problem (if you can add code to the domain class) is to add a method in the user class like the one below:
#JsonIgnore
public int getAge() {
return Period.between(
LocalDate.of(dobYear, dobMonth, 1),
LocalDate.now()
).getYears();
}
You can add the #JsonIgnore to block spring from exporting an "age" field when your entity is serialized. After adding that method you can create projection like the one below:
#Projection(name = "userAge ", types = {User.class})
public interface UserAge {
#Value("#{target.getAge()}")
Integer getAge();
}
Something like this, for example:
public class UserAgeDto {
private int yearOfBirth;
private int monthOfBirth;
public UserAgeDto(int yearOfBirth, int monthOfBirth) {
// constructor implementation...
}
public int getAge() {
// age calculation...
}
}
public interface UserRepo extends JpaRepository<User, Long> {
#Query("select new com.example.myapp.dto.UserAgeDto(u.yearOfBirth, u.monthOfBirth) from User u where u = ?")
UserAgeDto getUserAgeDto(User user);
}
Some info

Spring data Mongodb Store and query GIS Circle and Polygon

I've a simple use-case to solve using MongoDB with Spring-data connectors.
Store a polygon or circle (simple Point with radius)
Check if give point is inside polygon/circle
Now, I've done like this:
Store give Polygon as a geoJSON into a document
Apply 2dSphere index
Polygon stored as a Polygon geoJSON type
Circle stored a single point and radius as separate parameter
Polygons I'm able to query using $geoIntersects
Circle I'm able to query using $geoNear with aggregation
Problem:
What is right way to store and index Circle(Point and radius) and Polygon (multiple points)?
How to do a single query to check if given point reside in Polygon / Circle?
currently Spring Data does not support this kind of query ($geoIntersects) using MongoRepository interface, but this works for me:
Let's suppose that we have the following document
#Document
public class AreaTown {
#Id
private String id;
private String name;
#GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private GeoJsonPolygon area;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GeoJsonPolygon getArea() {
return area;
}
public void setArea(GeoJsonPolygon area) {
this.area = area;
}
#Override
public String toString() {
return "AreaTown [id=" + id + ", name=" + name + ", area=" + area + "]";
}
}
Applying a custom Query, I was able to found if a point is inside of a polygon
#SpringBootApplication
public class MongoGeoJsonApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(MongoGeoJsonApplication.class, args);
}
#Autowired
private MongoTemplate mongoTemplate;
#Override
public void run(String... args) throws Exception {
Point inside = new Point(-97.406285,18.481136);
Query query = new Query().addCriteria(Criteria.where("area").intersects(new GeoJsonPoint(inside)));
List<AreaTown> list = mongoTemplate.find(query, AreaTown.class);
System.out.println(list);
}
}

Spring dynamic Qualifier

I have an interface Shape
public interface Shape {
String draw();
}
And two implementations for the above Shape interface
#Component("triangle")
public class Triangle implements Shape {
public String draw() {
return "drawing Triangle";
}
}
and
#Component("circle")
public class Circle implements Shape {
public String draw() {
return "Drawing Circle";
}
}
In my client code if I have to decide which Shape class to use at run time based on Qualifier
#Autowired
private Shape shape;
#GET
#Produces(MediaType.TEXT_HTML)
#Path("/shape")
public String getShape(#PathParam("type")
String type) {
String a = shape.draw();
return a;
}
How to do it?
I want to pass "type" which I get as a pathparam to decide which Shape object to be injected at run time. Please help me in resolving this issue.
I could figure out one way of getting around this by injecting ApplicationContext in the client code.
#Autowired
private ApplicationContext appContext;
And looking up for the bean with the help of getBean method:
#GET
#Produces(MediaType.TEXT_HTML)
#Path("/shape/{type}")
public String getShape(#PathParam("type")
String type) {
Shape shape = appContext.getBean(type, Shape.class);
return shape.draw();
}
I hope this would help someone :)

Resources