I have this:
#Embeddable ClassA{
private String attributeA;
private String attributeB;
}
#Entity ClassB{
private String attributeC;
#Embedded ClassA extraAttrs;
}
Finally I have a CrudRepository from I want to get a query by using an attribute of ClassA, so I can't write this for example:
public interface TestCrud extends CrudRepository<ClassB, Long> {
public List<ClassB> findByAttributeA(String anA);
}
I can't do because attribute A is in the embedded ClassA instead off ClassB
How can I access from the CrudRepository interface definition to this attribute? Because this data it's in a single table, the purpose to put in a Embedded class it's because the attributes of ClassA are common to a lot of entitys, but not the PK, but now I need to access as I've said before.
Any ideas?
Thanks a lot to everyone who try to help me :),
This is actually very simple, you need to concat them together, in your case try this
findByExtraAttrsAttributeA(...)
Related
I have a project that I've recently inherited that has 2 tables that share a lot of common fields. I'm new to hibernate and want to know if I can use composition to generate the table instead of inheritance? B and D are basically the same class with a different table name.
Current Hierarchy is
B extends A extends BaseClass
D extends C extends BaseClass
My problem at the moment is that a lot of other classes extend BaseClass which don't have the shared fields and the 2 child classes don't share a common parent so I cannot add another level into the Hierarchy and use #MappedSuperclass.
Because of this I'd like to know if I can group my common fields into a single class and compose my child classes with this new class somehow?
Apologies for the cryptic names but as always; confidentiality...
edit - found something simmilar with #Embeddable https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/domain/embeddables.html
If you are using the JPA interface to Hibernate, you can use #Embedded and #Embeddable, to get more or less what you want. Be aware that the change will not be transparent: where you had:
#Entity
public class B extends A {
#Basic
private int foo;
...
}
that you referenced in JPQL by using b.foo, you will have:
#Embeddable
public classs Common {
#Basic
private int foo;
...
}
#Entity
public class B extends A {
#Embedded
private Common common;
...
}
which you will have to reference in JPQL using b.common.foo.
Read more about embeddable entities here:
https://en.wikibooks.org/wiki/Java_Persistence/Embeddables
You could potentially use #Embedded and embed the same object for both B and D, perhaps something like:
#Embeddable
public class CommonFieldObject {
#Column(name="COMFIELD1")
private String commonField1;
#Column(name="COMFIELD2")
private String commonField2;
...
}
#Table
public class C extends A {
#Embedded
#AttributeOverrides({
#AttributeOverride(name="commonField1", column=#Column(name="CFO_COMFIELD1")),
#AttributeOverride(name="commonField2", column=#Column(name="CFO_COMFIELD2"))
})
private CommonFieldObject commonFieldObj; //CFO_ prefix for this reference - in case we have a second field referencing a CommonFieldObject - use a different prefix..
...
}
You should then get the columns CFO_COMFIELD1 and CFO_COMFIELD2 in your table and you can recycle the CommonFieldObject for class D.
I can create a repository via defining an interface on the appropriate JPA class A like the following:
public interface ARepository extends CrudRepository<A, Long>
{
}
and I can use that in my Controller (for example) via
#Autowired
private ARepository aRepository;
and just can do things like this:
aRepository.save(..);
aRepository.findAll();
..
No problem so far.
But my problem is that I have ca. 500 JPA classes and need to access each table which means to define 500 Repositories in the style of above.
So does exist an thing to create that either dynamically via some Spring Data "magic" which from my point of view should exist otherwise the above would not be possible. It looks like this is similar to my problem.
Apart from that one more issue related to the above. I can define findBy... methods in the interface and in the background there will be generated a query method for this particular attribute. The question is also if this can be done in a dynamic way related to the previous question, cause I have groups of tables which need supplemental query methods..
There is spring-data-generator which can automatically generate the interfaces for you.
Regarding your 2nd question I don't think you that can be done in a dynamic way. Java is statically compiled and there's no way to add members dynamically. There could be a tool that generates code for those methods but if that tool generates methods for all combinations of columns you will end up with a huge amount of methods.
You can make a base abstract entity for your 500 classes an then create one repo for this class. (I think it's a common practice to have a BaseEntity class with id, version etc. for every entity in the project).
For simple repo methods (like save, findAll etc.) it will work right from the box (note - entities must have the equal id type). For example:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstarct class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
}
#Entity
public class Entity1 extends BaseEntity {
private String name;
}
#Entity
public class Entity2 extends BaseEntity {
private String name;
}
public interface BaseEntityRepo extends JpaRepository<BaseEntity, Long> {
}
Note that BaseEntity must have #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) to prevent of using singe table base_entity for every entity. And their ids must not intersect (see #GeneratedValue(strategy = GenerationType.SEQUENCE)).
Usage:
#RunWith(SpringRunner.class)
#SpringBootTest
public class BaseEntityRepoTest {
#Autowired private BaseEntityRepo repo;
#Before
public void setUp() throws Exception {
repo.save(asList(
new Entity1("entity1"),
new Entity2("entity2")
));
}
#Test
public void readingTest() throws Exception {
List<BaseEntity> entities = repo.findAll();
assertThat(entities).hasSize(2);
}
}
Related to your second question you can use this approach:
public interface BaseEntityRepo extends JpaRepository<BaseEntity, Long> {
<T> T findById(Long id, Class<T> type);
}
Usage:
#Test
public void findById() {
final Entity1 entity1 = repo.findById(1L, Entity1.class);
final Entity2 entity2 = repo.findById(2L, Entity2.class);
assertThat(entity1).isNotNull();
assertThat(entity2).isNotNull();
}
But you can build repo query methods only for 'common' properties of inherited entities which are present in the base class. To make this method work you must move the name parameter to the BaseEntity:
<T> List<T> findAllByNameLike(String name, Class<T> type);
I have a simple REST service that returns user profile entity that has about 20 fields.
I need to implement a functionality to filter the data where last name is required but all other fields (first name, age, city, state, zip, etc. ) are optional.
Is there a way to do it using JpaRepository without creating a lot of if/else statements for every single combination of patamenters?
It is a use case for JPA criteria (available since JPA2).
In indeed as you want to write a dynamic query, above all, you don't want to hard-coded JPQL queries for each combination and you don't want concatenating chunks of JPQL either as this is error-prone and not checked at compile time.
Note that in any case (Criteria or JPQL) you should check for each possible option if the client has specified it to be able to take them into consideration in the query build.
Now, as you implement the JPARepository interface, you have two ways :
using List<T> findAll(#Nullable Specification<T> spec); provided by the JpaSpecificationExecutor interface that you can also implement in your custom repository.
Enrich the JPARepository with your own interface that defines a method findAll() and that takes as parameter an object containing values for the research. Then create a concrete class to implement JPARepository.
You would have so the ability to inject the EntityManager and to use the Criteria API.
JpaRepository interface also implements QueryByExampleExecutor interface which provides findAll method for getting data using Query by Example (QBE) technique. That method would be really applicable for your scenario and is actually ideal when entity has a lot of fields and you want user to be able to filter entities by some of them.
Let's say the entity is Person and you want to create endpoint for fetching persons whose properties are equal to the ones which are specified. That could be accomplished with the following code:
Entity class:
#Entity
public class Person implements Serializable {
private Long id;
private String firstName;
private String lastName;
private Integer age;
private String city;
private String state;
private String zipCode;
}
Controller class:
#Controller
public class PersonController {
private PersonService service;
private PersonController(PersonService service) {
this.service = service;
}
#GetMapping
public List<Person> getMatchingPersons(#RequestBody Person personFilter) {
return service.findMatchingPersons(personFilter);
}
}
Service class:
#Service
public class PersonService {
private PersonRepository repository;
private PersonService(PersonRepository repository) {
this.repository = repository;
}
public List<Person> getMatchingPersons(Person personFilter) {
return repository.findAll(Example.of(personFilter));
}
}
Repository class:
#Repository
public class PersonRepository implements JpaRepository<Person, Long> {
}
It's always best to return a DTO representation of an entity in the
REST response.
In your DTO, you can only map required fields from an entity and
ignore other optional parameters.
Check this out http://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application
In my Hibernate+Spring project I've got several entities quite similar; that's why I'm using inheritance type: Table Per Class. The thing is similar to this
class CommonEntity {
private Integer id;
private String name;
private String description;
// Constructors & Setters & Getters
}
class InheritedClass1 extends CommonEntity {
private boolean active;
// Constructors & Setters & Getters
}
class InheritedClass2 extends CommonEntity {
}
As I'm using XML mapping, the mapping for the 1st inherited class contains both CommonEntity and InheritedClass1 fields/columns.
Everything's fine so far.
Here the question is, what would be the best way to implement Repositories/DAOs for the inherited entities? As they will contain common fields (for instance, probably it would end up in implementing a findByName DAO method).
My main point is to avoid implement the same findByName for all inherited class as the only difference would be the name of the Named Query (i.e. InheritedClass1_FindByName and InheritedClass2FindByName).
You could use generics to make a single DAO method that would work for any class which extends your CommonEntity like this:
public <T extends CommonEntity > T getByName(Class<T> clazz, String name) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(clazz, "named");
crit.add(Restrictions.eq("named.name", name));
return (T)crit.uniqueResult();
}
Obviously if name is not unique you would need to call crit.list() rather than uniqueResult()
Ok, lets say I have a model.
#Entity
public class Person extends Model {
public String name;
public String email;
}
Now. I have a couple of transient fields that don't exist in the database, but I need to access them. Basically they are fields for displaying the Person on a web page.
#Entity
public class PersonDisplay extends Person {
#Transient
public String DT_RowClass = "";
#Transient
public String cellColor = "";
}
Is it possible to query the PersonDisplay and get the Person object plus the PersonDisplay default values?
Like this.
PersonDisplay display = PersonDisplay.find("byEmail" , "test#test.com").first();
I'm running into a few errors when I do this.
Let me know if I need to explain this again.
Look up the #PostLoad annotation, which you can use to initialize any transient variables.