Spring bean name clash Hibernate/Proguard? - spring-data

Consider a multi-module Java/Scala Maven application to obfuscate. It's using the classic Spring approach of Hibernate/JPA/Spring Data JPA/services/repositories/.... All using xml configuration files.
What has been done
Keep intact class names mentioned in web.xml and Spring xml files.
Keep intact the package names used in all < context:component-scan.../ > + bean name generator to guarantee unique bean names.
Keep intact classes referenced by introspection.
Don't use mixed case class names because Spring bean names are case-insensitive.
But whatever I try, I continue to clash bean names with what I think is Hibernate. An example of the error:
Caused by: org.hibernate.DuplicateMappingException: Duplicate collection role mapping com.bar.project.Definition.d
at org.hibernate.cfg.Configuration$MappingsImpl.addCollection(Configuration.java:2739)
at org.hibernate.cfg.annotations.CollectionBinder.bind(CollectionBinder.java:561)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2084)
at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:895)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:728)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3625)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3579)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1381)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1786)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:96)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:915)
... 32 more
Proguard indeed made a class named 'd'. Which was duly renamed to something like 'BeanFFEECC' using the bean name generator.
class BeanNameGenerator extends DefaultBeanNameGenerator {
override def generateBeanName(definition: BeanDefinition, registry: BeanDefinitionRegistry): String = {
val id = Integer.toHexString(super.generateBeanName(definition, registry).hashCode).toUpperCase
var proposedName = "Bean" + id
while (registry.isBeanNameInUse(proposedName)) {
proposedName += "0"
}
proposedName
}
}
Unless I misunderstood the role of a Spring bean name generator, I would assume this should have fixed the name conflict. The very same error pops up when defining a class name dictionary for Proguard. To avoid it to generate one-letter class names.
Is there anybody who knows what problem I'm having here ?

FOUND IT ! Apparently Proguard will by default rename private fields. Which is common practise for entity classes. And since I've put the JPA annotations on the fields, you'll get a field named 'd' pretty frequently.
Adding the role to the configuration fixed that issue.
-keep class com.bar.model.** {
<fields>;
<methods>;
}
Before that it was
-keep class com.bar.model.**

Related

How to register custom Querydsl EntityPathResolver with Spring Data MongoRepositoryFactory?

I am using the Querydsl extension (QueryDslPredicateExecutor) to my CrudRepository.
To reliably exclude the generated Q classes from my test coverage measurements, they are generated into a dedicated querydsl subpackage of the respective domain classes (annotation processor option -Aquerydsl.packageSuffix=.querydsl).
Alas, this causes a ClassNotFoundException at application start up:
java.lang.IllegalArgumentException: Did not find a query class org.example.QDomain for domain class org.example.Domain!
at org.springframework.data.querydsl.SimpleEntityPathResolver.createPath(SimpleEntityPathResolver.java:63)
at org.springframework.data.mongodb.repository.support.QueryDslMongoRepository.<init>(QueryDslMongoRepository.java:85)
at org.springframework.data.mongodb.repository.support.QueryDslMongoRepository.<init>(QueryDslMongoRepository.java:67)
…
Caused by: java.lang.ClassNotFoundException: org.example.QDomain
…
I have already located the EntityPathResolver interface that supposedly would allow me to plug in my own domain class to Q class mapping that inserts the .querydsl package suffix, but I haven’t found a way to configure Spring Data’s MongoRepositoryFactory to pick my own EntityPathResolver.
Is this possible?
Currently, the only way is to create your own variant of the MongoRepositoryFactory because the instance of the EntityPathResolver is hard-wired into it.

Can JpaRepository take entity description from *.hbm.xml files?

I write application using hibernate, spring-boot-data-jpa and spring-boot-data-rest.
My entity classes are not contain any annotations, and all orm mapping placed in several hbm.xml files.
class MyEntity {
Long id;
String name;
}
interface MyRepository extends JpaRepository<MyEntity, Long> {
}
Hibernate working fine, as well as all methods of JpaRepository like findOne. The problem that the rest interface provided by JpaRepository say
"PersistentEntity does not have an identifier property!".
I found that adding #Id to MyEntity class solves the problem. However, I prefer define orm mapping in hbm.xml file, not using annotations.
How can I configure JpaRepository's to consider *.hbm.xml files?
I faced similar problem with JpaRepository using hbm files. JpaRepository can accept hbm files if these are present in resource folder. In resources folder, create any folder say resources/hbm and move all hbm file to this folder. Now hbm files will be found by JpaRepository and not an managed type error will be fixed.

Spring Data JPA repository methods don't recognize property names with underscores

I have underscores in the entity property names, and when Spring tries to create the JPA repository implementation, it results in an exception trying to resolve the name of the property.
Entity:
#Entity
public class Student {
#Id
private String s_id;
private String s_name;
...
}
Repository:
#Repository
#Transactional
public interface StudentRepository extends CrudRepository<Student, String> {
List<Student> findByS__name(String name);
}
Exception:
org.springframework.data.mapping.PropertyReferenceException:
No property s found for type Student
It is said here http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
If your property names contain underscores (e.g. first_name) you can
escape the underscore in the method name with a second underscore. For
a first_name property the query method would have to be named
findByFirst__name(…).
I just did as document said, but I still got the exception.
I dont want write #Query by myself, and I need underscore in my property name, how to fix this problem?
I use Spring data jpa 1.8.0.RELEASE + hibernate 4.3.9.Final
Avoid using underscores in the entity property names if you have control over the property naming. This will resolve your repository woes, and will result in a cleaner code-base. Developers dealing with the code after you will thank you.
Note, it's not just my opinion: Spring specifically discourages using underscores.
As we treat underscore as a reserved character we strongly advise to
follow standard Java naming conventions (i.e. not using underscores in
property names but camel case instead).
this JIRA issue shows why the documentation was updated with this reccomendation, and the part describing the double underscore option were removed.
I suspect your root problem is that Spring/Hibernate is not mapping camel case property names to the snake case names you have for your columns in the database. What you really need is for your property name to be interpreted in the SQL that hiberate generates as S_NAME.
Is that why underscores in your property name are "required"? If so, there are a few solutions:
Option 1: #Column annotation
To get JPA/Hibernate to map to the correct column names you can tell it the names explicitly. Use the annotation #Column(name="...") to tell it what column names to use in SQL. Then the field names are not constrained by the column names.
#Entity
public class Student {
#Id
#Column(name="s_id")
private String sId;
#Column(name="s_name")
private String sName;
//...getters and setters...
}
Option 2: Improved Naming Strategy
Or if your application has a large number of entities, rather than adding #Column to every property, change the default naming strategy in your configuration file to the hibernate improved naming strategy.
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
This naming strategy will convert camelCase to SNAKE_CASE. Then your class could look as simple as this:
#Entity
public class Student {
#Id
private String sId;
private String sName;
//...getters and setters...
}
Using either of those options, when it creates the SQL it will resolve the column names to:
S_ID
S_NAME
Note: If you are using, or can use Spring Boot, the auto-configuration default will use SpringNamingStrategy, which is a slightly modified version of the hibernate improved strategy. You won't have to do anything to get this improved naming strategy.
The finish line:
Using camel case in your property names you can write your repository method name using camel case, and you can stop trying to wrangle the double underscore:
#Repository
#Transactional
public interface StudentRepository extends CrudRepository<Student, String> {
List<Student> findBySName(String name);
}
Writing double underscore i.e. writing findByS__Name() for property name s_name just does not work. I have tried and tested it. Go by the above answer and change the name of existing instance variables in your entity class. Just dont change getters and setters as they might be used in the existing code.
If you cant change the entities which was my case then better use jqpl query or native sql query on top of repository method
#Query("select s from Student s where s.s_name=?")
List<Student> findBySName();

Eclipse - can you navigate to an injected bean

I am using the STS eclipse IDE version 2.8
Is it possible to navigate directly to a class from the bean name ?
IE to the class whose bean name is 'foo.bar in the example below:
public class SomeClass() {
#Autowired(required=true)
#Qualifier("foo.bar")
private FooServive myFooService;
...
}
I know I can get to it from the Spring Explorer tab but I want to miss out the middle man.
...often, also the "Spring Beans references search" is quite helpful. It provides an overview of all references to the given bean name; typically you'll get the class definition, the XML definition and uses in dependency injection.
Standard keybinding for this is CTRL-SHIFT-g.
You can use Alt-F7 to Java-search on "foo.bar". It will bring up the bean definition so named (at least when using XML configuration files). At the bean definition you can press F3 on the value of the class attribute, which will bring you to the class definition itself. You have to have the Spring Nature enabled of course.

cannot find my bean using the InitialContext.lookup() method

I have tried to use struts 1.3 API to make a small application with EJB 3.0. Unfortunatelly i cannot use the #EJB annotation to call my bean object from inside my action class. I have solved this problem using different workarounds ( the first one is to use my global jndi name of my bean and the other is to call another class first and use the #EJB annotation from that class). Still these two workarounds have significant disadvantages. I would like to call my EJB directly from my action class. I have read plenty examples using the "java:comp/env/beanName" JNDI name but still haven't figure out how to do it and get name not found axception.
Let the full name of the local EJB class be the com.ejb.myEjbPackage.MyEJBLocal, how can i call it using the context lookup? (can i do it without modifying any of the web.xml and sun-web.xml descriptors?)
I am using glassfish server and Netbeans IDE.
Thank you in advance
#EJB won't work in a standard pojo it can only be done in a managed object (i.e. another session bean)
So...
Here's your bean
#Stateless(mappedName="beanName")
public class beanName implements beanNameRemote {
Here's your lookup
Context context = new InitialContext(); //default lookup pulls from jndi properties file
context.lookup("beanName");
You can do some further reading on the mappedName to see if you want to use it or not.
I found the answer :
If you cannot use the EJB annotation in the class you want to call the bean then :
If you don't want to mess with XML descriptors to define your bean , you have to do it in the bean class itself.
Hence i used the following annotation in the GameBean class
#Stateless
#EJB(name="ejb/GameBean",beanInterface=GameBeanLocal.class,beanName="GameBean")
public class GameBean implements GameBeanLocal {.....
The beanName is optional. The annotation must be declared in the line ABOVE the declaration of the class.
Then, in order to call the bean from the other class you can do
InitialContext ic = new InitialContext();
ic.lookup("java:comp/env/ejb/GameBean");