I'm working in a project that's already grown up, so I won't be able change any version of any of the used frameworks or libraries.
Actually, I had a particular situation that I managed to resolve with two entities and #Inheritance, #DiscriminatorColumn and #DiscriminatorValue. Now, I have an entity with references to both classes in the inheritance, like this:
#Entity
//Other annotations
public class A implements IA {
//Class body
}
#Entity
//Other annotations
public class B extends A implements IB { //Note: `IB` extends `IA`.
//Class body
}
#Entity
//Other annotations
public class Container {
private IA object1;
private IA object2;
}
The problem here is that I'm trying to use #Any annotation, because either object1 and/or object2 can be of type A or B. What I've done is this:
#Any(metaColumn=#Column(name="objectOneType", length=3),fetch=FetchType.EAGER)
#AnyMetaDef(idType="long",metaType="string", metaValues={
#MetaValue(targetEntity=A.class, value="A"),
#MetaValue(targetEntity=B.class, value="B")
}
)
#JoinColumn(name = "relatedObjectId")
private IA object1;
As stated here, Hibernate's #Any annotation family has no counterpart in JPA 2 and, since I can't switch to Hibernate 4.1, I'm stuck here trying to figure out a way to make it work or what should I do to fix it.
A "not so elegant" way would be to create a new entity and copy the fields from A to B, erasing the inheritance and fixgin the particular cases.
Maybe, I'm losing some particular feature in JPA that I don't know yet, so I'm currently open to and thank any kind suggestion that might point me in the right direction.
You don't need any #Any annotation. B extends A, so B is an A. So the following is sufficient:
public class Container {
#ManyToOne
private A object1;
#ManyToOne
private A object2;
}
(assuming the association is a ManyToOne. It could also be a OneToOne)
Hibernate will figure out the concrete type of both objects all by itself, and will initialize object1 and object2 with an instance of A or B.
Related
I have a requirement that based on profile I need to inject 2 different classes into DAO layer to perform CRUD operation. Let's say we have class A and Class B for profiles a and b respectively. Now in the DAO layer without using if else condition (As I am using that currently based on the profile, I am using service layer to call 2 different methods 1.saveA(), 2.saveB().) But is there any way to make it more generic and based on profile or either by the class reference I can instantiate different entity as well as JPA Classes? I tried to use
<T extends Parent> T factoryMethod(Class<T> clazz) throws Exception {
return (T) clazz.newInstance();
}
but this also will force me to cast the returned object to a class. I tried creating a parent P for both class A and B. and used them instead but got confused when injecting the entity types to JPARepository.
I tried creating a SimpleJPARepository but didnt worked as there are overridden methods in ARepository and BRepository.
Or,
is there a way I can use the same entity class for 2 different tables? that way it can be solved. for 1 profile I have different sets of columns whereas for 2nd profile I have different columns.
this is how I am expecting: Would it be possible? or, how I am doing now is correct?
public void doStuff(Class<T> class){
GenericRepository repo;
if(class instanceof A){
//use ARepository;
repo = applicationContext.getBean(ARepository);
}else{
//use BRepository;
repo = applicationContext.getBean(BRepository);
}
repo.save(class);
repo.flush();
}
You can create a method utility like following: The key is the class type of the entity and the value is the repository.
Map<Class<? extends Parent>, JpaRepository> repoMapping = new HashMap<>();
#PostConstruct
public void init(){
repoMapping.put(A.class, applicationContext.getBean(ARepository));
repoMapping.put(B.class, applicationContext.getBean(BRepository));
}
public JpaRepository getRepo(Class<? extends Parent> classs){
return repoMapping.get(classs);
}
I'm learning how to extend Spring's CrudRepository interface to create a repository for an entity. But I'm having trouble implementing more complicated queries that use values that aren't hard-coded. Here's a contrived example. The HQL is not valid, but it shows what I'm trying to do:
import mypackage.DogTypeEnum;
public interface myRepository extends CrudRepository<Dog, Integer> {
int oldAge = 10; // years - old for a dog
#Query(SELECT dog From Dog dog WHERE dog.age > oldAge and dog.type = DogTypeEnum.poodle
public List<Dog> findOldPoodles()
}
So in the above example, I'm trying to query for all dogs of type poodle that are over a certain age threshold. I don't want to hard code either poodle or the value 10 because these values that will be used elsewhere in the code as well and I want to avoid duplication. I don't want to require the user to pass those values in as parameters either.
Is there a way to do this?
You could create a Interface that extend your repository as like this:
//Only complex querys
public interface MyRepositoryCustom {
List<Dog> findOldPoodles()
}
// Your Repository must extends to MyRepositoryCustom
public interface MyRepository extends CrudRepository<Dog, Integer>, MyRepositoryCustom {
// Declare query methods
}
//More complex query
public class MyRepositoryImpl implements MyRepositoryCustom {
#PersistenceContext
private EntityManager em;
public List<Dog> findOldPoodles() {
Query query = em.createQuery("SELECT dog From Dog dog WHERE dog.age > :oldAge and dog.type = :type");
query.setParameter("oldAge",10);
query.setParameter("type",DogTypeEnum.poodle.name);
return query.getResultList();
}
}
Remember all java class starts with a Upper Case letter.
This link can help you: Spring repositories
In my case I'll never have this problem, if you structure your project as the next package architecture shows, you won't have this problem:
View (Angular, JSP, JSF...) -- APP
Controller -- APP
Services -- Main Core
DAO -- Main Core
Entities -- Main Core
This way you make more modular, escalable, maintainable and comprehensive application.
It doesn't matter what technology you use in your view, you just have to invoque the correct method on the service.
In here, on the services package you could have a service for example:
#Service
public class ServiceDog extends Serializable {
#Autowired
private MyRepository myRepository;
int oldAge = 10;
public List<Dog> findOldPoodles() throws ServicioException {
return myRepository.findAllByAgeGreaterThanAndType(oldAge, DogTypeEnum.poodle);
}
}
Now you could use all the advantage you get from using spring-data-jpa reference or make a more simple JPQL Query.
This is a simple example but this way you make sure that each DAO speak with only one entity (It will only have the methods needed for this entity like save, delete.. with no dependencies with other DAOs), and the service are the ones on calling the different DAOs and make the necessary actions on them.
Hope this helps.
I'm trying to persist some enums in Hibernate and it looks like my two options for built in support are to use the name of the enum, which I would rather not do because it's string based instead of int based, or the ordinal of the enum, which I would rather not do because if I add one of the enum values at the top of the class later on, I break everything down the line.
Instead, I have an interface called Identifiable that has public int getId() as part of its contract. This way, the enums I want to persist can implement Identifable and I can know that they'll define their own id.
But when I try to extend EnumValueMapperSupport so I can utilize this functionality, I'm greeted with errors from the compiler because the EnumValueMapper interface and the EnumValueMapperSupport class are not static, and thus are expected to be locked into a given EnumType object.
How can I extend this functionality in Hibernate, short of rewriting a bunch of Hibernate code and submitting a patch. If I can't, is there another way to somehow store an enum based on something other than the ordinal or name, but instead on your own code?
In a related thought, has anyone personally been down this road and decided "let's see how bad the name mapping is" and just went with name mapping because it wasn't that much worse performance? Like, is it possible I'm prematurely optimizing here?
I'm working against Hibernate version 5.0.2-final.
At least for Hibernate 4.3.5 the EnumValueMapper is static - although private.
But you can extend EnumValueMapperSupport in an extension of EnumType:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
}
To create an instance of this mapper you need an instance of your EnumType:
ExampleEnumType type = new ExampleEnumType();
ExampleMapper mapper = type.new ExampleMapper();
Or you create it inside your type:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
public ExampleMapper createMapper() {
return new ExampleMapper();
}
}
I have a class C extends B. and B extends A. A has an attribute name. I don't want to see name attribute at C class.
How can I do that ignore?
PS: If it is not possible at Java: I am working on a Spring project that uses Apache CXF and has a web service capability. I want to get B type object from client and send C type object to client. Because of my design issues I don't want to change my inheritance mechanism. If there is a way can I ignore name field at C class? I am implementing as first-code style.
You can declare name field in A as private and keep both A and B in same Package.
package A;
Class A {
protected String name;
}
Class B extends A{
// B can access A's attribute
}
package C;
Class C extends B{
// C cannot access name attribute defined in A.
}
And keep C is in a different package.
Please read about composition over inheritance. If you need to hide some elements then probably you do not understood properly the Object Oriented code design.
The example provided by you is too narrow to create any better answer. But what is the point of hiding in C when, you can cast it to A and still use it ?
I have a class C extends B. and B extends A. A has an attribute
name. I don't want to see name attribute at C class.
Based on your edit, you are trying to hide an attribute from a superclass (A) in the serialized representation of one of it's subclasses (C). Assuming you are using JAX-WS/JAX-RS, you can do this by overriding the property in the subclass and applying annotations to mark it as not eligible for serialization. The primary annotation to apply will be the JAXB annotation #XmlTransient. You can also #JsonIgnore the property, if you are providing the option to serialize objects as JSON. Pseudo code shown below:
#XmlRootElement
public class A implements Serializable {
private String name;
// ...
}
#XmlRootElement
public class C extends A {
#Override
#XmlTransient
public String getName() {
return super.getName();
}
}
I'm trying to implement a inheritence relationship between JPA entities.
Borrowing the example from:
http://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_mapping_discrim.html
#Entity
#Table(name="SUB", schema="CNTRCT")
#DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
public abstract class Subscription {
...
}
#Entity(name="Lifetime")
#DiscriminatorValue("2")
public class LifetimeSubscription
extends Subscription {
...
}
}
#Entity(name="Trial")
#DiscriminatorValue("3")
public class TrialSubscription
extends Subscription {
...
}
What I need to be able to do is have an additional entity that catches the rest, something like:
#Entity(name="WildCard")
#DiscriminatorValue(^[23])
public class WildSubscription
extends Subscription {
...
}
Where if it does not match LifetimeSubscription or TrialSubscription it will match WildSubscription.
It actually makes a bit more sense if you think of it where the wild is the superclass, and if there is not a more concrete implementation that fits, use the superclass.
Anyone know of a method of doing this?
Thanks!
The JPA API allows only plain values here, and for a reason: discriminator values are mapped to SQL WHERE:
SELECT ... WHERE kind = 1
If you could specify regular expressions here, it wouldn't be transferable to SQL, as it does not support such constructs.