Situation
I inherited some code from a developer that seems to have used undocumented features of spring.
Specifically, it is related to using #projection functionality without actually annotating the interfaceas such.
A specific example is the following:
public interface DirType extends KeyValInterface {
#Value("#{target.id}") String getId();
#Value("#{target.code}") String getText();
}
This should not work according to the official documentation, but it does.
public interface DirTypeRepository extends JpaRepository<ABDirType, Long> {
List<DirType> findAllSummarizedBy();
}
So the method findAllSummarizedBy() of the respository is actually sending a list of DirType.
And it is using the #Valueannotations to do the mapping, but without a #Projection annotation.
The Entity class is the following:
#Data #Entity #Table(name="dir_type") #AllArgsConstructor #NoArgsConstructor
public static class ABDirType {
private #Id #GeneratedValue Long id;
private String code;
}
Question
Does anyone have any more information about this undocumented feature related to Projections and not annotating the interface as a #Projection ?
Is this possible in all versions or is it a hidden hack that is risky to use?
In Spring Data JPA (which you seem to be using here), you can simply use an interface with appropriate getters as projection targets.
See the documentation about this (esp. "Example 62. Simple Projection").
There does not seem to be a #Projection annotation anywhere in the documentation.
Related
As I am gradually trying to remove Dependencies on Spring in the domain part of my library without minimal extra effort, I now turn to Spring Data and the Repositories
Originally we annotated our domain entities to look like this:
#Document
public void MyEntity {
#Id
#Getter private final EntityIdentifier identifier;
#PersistenceConstructor
public MyEntity( ... ) {}
...
}
and so on.
where #Document, #PersistenceConstructor and #Id originate from the Spring Project and some are for a specific database backend (MongoDB).
I would like to cut this dependency and use my own annotations, that make sense in my domain - #Document is definitly nothing my domain experts would understand when appearing on e.g an clas Chair or a Desk.
For de/serialization with Jackson, I can create mixins to add specific annotations to classes without modifying them in their origin.
Maybe there is a similar technique for Spring or some other way to achive this that is more elegant than creating a wrapping class?
Apparently I need some clarification:
Lets suppose we try to write a clean architecture application which consists out of the following modules: domain, adapters, application. In the domain module, I have my domain logic and domain entities and everything domainy. I do not have anything springy - no dependency on spring whatsoever, not even by having a dependency that somehow depends on spring.
In the adapters and application module, I do have dependencies on spring. I might use spring-data to implement the Repository-Adapters. I will use Spring to configure and glue together the application.
Now, in my domain module I have the following classes:
#AllArgsConstructor
#HashAndEquals(of="identifier")
#DomainEntity // <-- This is an Annotation that has no dependency on Spring!
public class DomainEntity {
#DomainId // <-- This is an Annotation that has no dependency on Spring!
#Getter private final DomainEntityIdentifier identifier;
#Getter #Setter private String someValue;
...
}
#HashAndEquals
#AllArgsConstructor
public class DomainEntityIdentifiers {
#Getter private final String name;
}
public void interface DomainEntityRepository {
DomainEntity findById(DomainEntityIdentifier identifier);
void save(DomainEntity domainEntity)
void deleteById(DomainEntityIdentifier identifier);
}
Now the task is, to provide the implementation of that interface in the adapters module, using Spring Data - e.g. spring-data-mongo and inject this adapter to the domain in the application module.
Now, surly I can create an class, lets say DomainEntityMongo which is basically the same as the DomainEntity just with the spring-data-mongo-annotations, then a public interface MyEntityRepository extends CrudRepository<EntityIdentifier, MyEntityMongo> and implement the interface DomainRepository by using MyEntityRepository and converting there and back again between DomainEntityMongo <=> DomainEntity.
What I am looking for is a more magical/generical solution. E.g.
Having jackson-style mixin-classes, which provide Spring with the necessary/missing meta-data to do the work
Configuring Spring to use non-spring-annotations to do the work (just as it is possible with the ComponentScan for non-component-inheriting Annotations)
Or - if the data guys have crafted another innovative solution - this innovative solution.
you can use
#JsonDeserialize(using = yourCustomizedDeserializer.class)
have a look here
https://www.baeldung.com/jackson-deserialization
you can customize your persistence strategy with #Persister. You may, for example, specify your own subclass of org.hibernate.persister.EntityPersister or you might even provide a completely new implementation of the interface org.hibernate.persister.ClassPersister that implements persistence via, for example, stored procedure calls, serialization to flat files or LDAP.
is that what you are looking for?
So I have an AppUser class:
#Data
#Builder
#Document(collection = "app_users")
#Component
#AllArgsConstructor
#NoArgsConstructor
#Import(AppConfig.class)
public class AppUser {
#Id
#NotBlank(message = ErrorConstants.ANDROID_USER_ACCOUNT_MANAGER_ID_IS_NULL)
private String androidUserAccountManagerId;
#NotBlank(message = ErrorConstants.NULL_NAME)
private String name;
private Friend bestFriend;
#Setter(AccessLevel.NONE)
private FriendList friendList;
private boolean manualBestFriendOverride;
public Optional<Friend> getFriend(String friendName) {
return friendList.getFriend(friendName);
}
public void calculateBestFriend() {
if (!manualBestFriendOverride) {
bestFriend = friendList.calculateAndReturnBestFriend();
}
}
}
I have created an AppUserRepository interface that extends MongoRepository:
#Repository
public interface AppUserRepository extends MongoRepository<AppUser, String> {}
I have a WebController class that interacts with the interface. The AppUserRepository field in this class is #Autowired. This all seems to work but I have a few questions regarding how, and how I go forward and write integration tests for this:
How do I configure this AppUserRepository that has been created? Can I run it on a specific port etc?
Why has the Autowiring worked as I have not created this AppUserRepository Bean in an AppConfig like I have other Beans that are Autowired in my application.
If I was to create a Bean, wouldn't I also have to implement the class and return the instantiation? I started doing this but I had to implement all of the MongoRepository classes methods which I wasn't sure seemed quite right.
How do I write integration tests with an AppUserRepository? I need an AppUserRepository for my requests to interact with, but I do not want this to be the same DB as the real-time application DB when the service is up and running. Can I #Autowire the database into the integration test class and then close the DB after the integration tests run? If this is how I go forward, I think I then need to do point 3 above.
Thanks for your help in advance, I have tried reading some documentation but I think I am missing some key knowledge that means it is all quite overwhelming and confusing.
Thanks!
That's actually quite a big story to tell. This topic is called Spring Data JPA, Hibernate. You might wanna do a research on that, and watch some tutorials and so on.
Briefly, that MongoRepository just gives you a lot of methods which you can use. You can also define your own methods, add queries and etc.
Your starting points: https://www.baeldung.com/spring-boot-hibernate
https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa
https://www.baeldung.com/spring-data-jpa-query
Of course you can set a port number (and some other properties) via application.properties file. This is a list of most common properties, you can find properties for mongodb on it:
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
Now about bean. You basically created one with #Repository annotation actually. So Spring Context loads it on the start of application. You can autowire it.
Imagine the following situation when using GWT, RequestFactory and JPA. The server side:
#Entity
public class SuperEmployee implements IsEmployee {...}
#Entity
public class BadEmployee implements IsEmployee {...}
#Entity
public class Supervisor {
List<IsEmployee> employees;
...
}
The client side proxies:
#ProxyFor(value = Supervisor.class, ...)
public interface SupervisorProxy {...}
Now I'd like to have something similar to:
#ProxyFor(value = IsEmployee.class, ...)
public interface EmployeerProxy {...}
Of course this isn't working, but what would be the solution if I'm not interested in the concrete type on client side and just want to work with the interface methods.
I've read the following articles that might be helpful (but maybe I didn't get the point right):
http://www.gwtproject.org/doc/latest/DevGuideRequestFactory.html (section "Transportable types").
Thanks for enlightening my Requestfactory knowledge!
Ralf
GWT does not (yet) support #ProxyFor pointing to interfaces. This will be possible in 2.7 though.
In the mean time, you have to use a base class on the server-side, or a hierarchy of interfaces on the client-side (possibly with #ExtraTypes pointing to the specific interfaces if they're not used directly otherwise)
I'm rolling my own IAuthorizationStrategy for Wicket 1.5.x I've setup type annotation for pages to use with isInstantiationAuthorized(). It works well and I'd like to use annotations for isActionAuthorized() as well. Ideally I'd like to be able annotate local variables and then check the annotations in my AuthStrategy. From what I've read Local variable Annotation doesn't work that way.
Is there any kind of known work around, maybe some sort of Compile time annotation processing to turn an annotated local variable into an "anonymous" subclass with the annotation as a type annotation?
For the record, the annotation I'm trying to use looks like this:
#Retention(RetentionPolicy.Runtime)
#Target(ElementType.Type, ElementType.LOCAL_VARIABLE)
public #interface AdminOnly
{
int isVisible() default 0;
int isEnabled() default 1;
}
UPDATE
So based on #Xavi López'es answer what I was hoping to do isn't exactly possible.
Annotated LocalVariables should be available at compile time though. Is there some way maybe I could use them as a shortcut for boiler-plating the meta-data code examples that are available in Wicket Examples or the excellent Apache Wicket Cookbook?
I've struggled with a similar issue some time ago with Wicket 1.3.x, and didn't find any way to achieve this with annotations. Annotations on local variables can't be retained at run-time, as explained in the JLS (9.6.3.2. #Retention):
An annotation on a local variable declaration is never retained in the binary representation.
In this related question: How can I create an annotation processor that processes a Local Variable? they talked about LAPT-javac, a patched javac version to allow this. On their site there's a link to the Type Annotations Specification (JSR 308), which will hopefully address this subject (JDK 8 ?).
I ended up defining a plain old interface with a related functionality code:
public interface RestrictedComponent {
Integer getFunction();
}
The main problem with this approach is that it's not possible to make instant anonymous subclasses of a specific class implement other interfaces (such as Component c = new TextField() implements AdminOnly { }) , but you can always define Component extensions that just implement RestrictedComponent in a class:
public abstract class RestrictedTextField extends TextField implements RestrictedComponent { }
Finally, I ended up implementing a RestrictedContainer that just subclassed WebMarkupContainer and put every secured component inside one, modelling it with a <wicket:container> in the markup.
public class RestrictedContainer extends WebMarkupContainer implements RestrictedComponent {
private final Integer function;
public RestrictedContainer(String id, IModel model, final Integer function) {
super(id, model);
this.function = function;
}
public RestrictedContainer(String id, final Integer funcionalitat) {
super(id);
this.function = function;
}
public Integer getFunction() {
return function;
}
}
And then in the Authorization Strategy checked for component instanceof RestrictedComponent and returned true or false depending on user permissions on the associated function.
I have this interface:
public interface IDbTable extends Serializable
{
public int getId();
}
I need to obligate all the classes that implements IDbTable, to have an annotation "#DatabaseTable" and at least one field inside class that have "#DatabaseField".
The only way of implementing IDbTable needs to be like this:
#DatabaseTable(tableName = "Something")
public static class TestTable implements IDbTable
{
#DatabaseField
public int id;
public int getId()
{
return id;
}
}
Is this possible with interface or inheritance?
Today i have an unit test that scan all the classes and check this requirements. Is this a good practice?
You can't apply annotations to an interface that will be inherited by the implementing class, according to Java documentation: http://docs.oracle.com/javase/6/docs/api/java/lang/annotation/Inherited.html
Indicates that an annotation type is automatically inherited. If an
Inherited meta-annotation is present on an annotation type
declaration, and the user queries the annotation type on a class
declaration, and the class declaration has no annotation for this
type, then the class's superclass will automatically be queried for
the annotation type. This process will be repeated until an annotation
for this type is found, or the top of the class hierarchy (Object) is
reached. If no superclass has an annotation for this type, then the
query will indicate that the class in question has no such annotation.
Note that this meta-annotation type has no effect if the annotated
type is used to annotate anything other than a class. Note also that
this meta-annotation only causes annotations to be inherited from
superclasses; annotations on implemented interfaces have no effect.
Why don't you use annotation processing, to check that the requiered annotation is present in your class when it is sent to your framework, or at build/deployment time.