Why JPQL ignore parent's fields? - jpa

App's stack: Hibernate, Spring Data, JPA.
There are some entities in the app. I try make JPQL-query in repository of my class OpenParagraph.
OpenParagraph:
#Entity
#Table(name = "open_paragraphs")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
public class OpenParagraph extends ProgramEntry {
#NotNull
#Column(name = "sort_num")
private Integer sortNum;
}
OpenParagraph has a parent: abstract class ProgramEntry.
ProgramEntry:
#MappedSuperclass
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
public abstract class ProgramEntry extends AbstractBaseEntity {
#NotNull
#ManyToOne
#JoinColumn(name = "paragraph_id")
private Paragraph paragraph;
#NotNull
#ManyToOne
#JoinColumn(name = "program_id")
private Program program;
}
So, i tring to appeal to OpenParagraph's field "Paragraph", but IDEA tells me it's mistake:
It doesn't offer me the "program" field:
IDEA offer fields only from OpenParagraph, not from parent.
My question: this is IDEA's fail? If this is'nt IDEA's fail, then how i can call "program" in this query?

This is/was a bug of Intellij IDEA (maybe related to this?). But:
It is possible to query by fields of the super class (or MappedSuperclass). Here is an example:
#MappedSuperclass
#Getter
#Setter
public class Foo extends AbstractPersistable<Long> {
#Column
private String fooValue;
}
#Entity
#Getter
#Setter
public class Bar extends Foo {
#Column
private String barValue;
}
public interface BarRepository extends JpaRepository<Bar, Long> {
#Query("SELECT b FROM Bar b WHERE b.fooValue = ?1")
List<Bar> findByFooValue(String fooValue);
}
Given this, calling the repository method, something like this will be logged (with enabled sql logging):
Hibernate: select bar0_.id as id1_0_, bar0_.foo_value as foo_valu2_0_, bar0_.bar_value as bar_valu3_0_ from bar bar0_ where bar0_.foo_value=?
Hint:
If you are using Spring Boot (with the test dependency/dependencies and an embedded test db like h2), it is quite easy to execute such methods without to run the whole application. Here just a small snipped that would execute the method (even though this is no test, but that's enough to call methods somehow):
#SpringBootTest
public class BarRepositoryTest {
#Autowired
BarRepository barRepository;
#Test
public void testFindByFooValue() {
barRepository.findByFooValue("foo");
}
}

Related

Querying revisions of nested object using spring-data-envers

I'm trying to implement entity auditing in my Java Spring Boot project using spring-data-envers. All the entities are being created as they should, but I've come up against a brick wall when executing the query.
parentRepository.findRevisions(id).stream().map(Parent::getEntity).collect(Collectors.toList());
During this select the repository is supposed to fetch info also from the child entity, instead I get unable to find <child object> with {id}.
According to my experiments categoryId is being searched in the Category_Aud table, instead of the actual table with desired data.
Code snippets:
#Data
#Entity
#Audited
#NoArgsConstructor
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private Status status;
#Enumerated(EnumType.STRING)
private Type requestType;
private String fullName;
#ManyToOne
#JoinColumn(name = "child_id")
private Child child;
}
#Data
#Entity
#Audited
#NoArgsConstructor
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
}
I've extended Parent with RevisionRepository
#Repository
public interface ParentRepository extends RevisionRepository<Parent, Long, Long>, JpaRepository<Parent, Long>
And annotated my SpringBootApplication entry class with:
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
I couldn't find any explanation for this so far, how can make parentRepository get what I need?
The underlying problem here is that the reference from a versioned entity isn't really properly defined. Which variant of the reference should be returned? The one at the start of the version you use as a basis, the one at the end? The one that exists right now?
There are scenarios for which each variant makes sense.
Therefor you have to query the revisions yourself and can't simply navigate to them.

Query for joins in Spring JPA

I have the below entities
#Entity
#Getter
#Setter
public class Aggregate {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "aggregate")
private Set<Single> singleSet;
}
#Entity
#Getter
#Setter
public class Single {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
private Integer number;
#ManyToOne
#JoinColumn(name = "agg_id")
private Aggregate aggregate;
}
I also have the below repository
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
}
I want to return all associated Single records where number in object Single is equal to some random number
I am assuming that the query will be something like this
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
public List<Single> findBySingleSet_Number(Integer number);
}
However when I try to use Intellij to complete my named query it always populates like this
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
public List<Single> findBySingleSet_Empty_Number(Integer number);
}
I am wondering what the Empty stands for ?
Also should I create another Single repository since the query is related to returning Single records.

microservice seems to work, but there's no data displayed

I have a simple app that's supposed to connect to postgres and display content of one of the tables.
I installed postgres, created a table and inserted a row, but nothing is shown when I run it.
This are my application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=name
spring.datasource.password=pass
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
and this is repository interface
#Repository
public interface TaxonRepository extends CrudRepository<Taxon, Long> {
}
and the model
#Entity
#Table(name = "dim_taxon")
public class Taxon{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Getter #Setter
private Long id;
#Getter #Setter
private String name;
#Getter #Setter
private String value;
#Getter #Setter
private String reserve;
#Getter #Setter
private String source;
}
My service
#Service
public class TaxonService implements TaxonServiceI{
#Autowired
private TaxonRepository repository;
#Override
public List<Taxon> findAll() {
return (List<Taxon>) repository.findAll();
}
}
and controller
#Controller
public class TaxonController {
#Autowired
private TaxonServiceI taxonService;
#RequestMapping(value="/showTaxons")
public String homePage(Model model){
List<Taxon> taxons = taxonService.findAll();
model.addAttribute("taxons", taxons);
return "index";
}
}
I tried to add an object manually to check if there was a problem with the html or smth
List<Taxon> taxons = new ArrayList<>();
Taxon taxon1 = new Taxon();
taxon1.setName("a");
taxon1.setReserve("a");
taxon1.setSource("a");
taxon1.setValue("a");
taxons.add(taxon1);
model.addAttribute("taxons", taxons);
but html is fine. Seems like this
List<Taxon> taxons = taxonService.findAll();
doesn't work. What's the problem here? There aren't actually any errors.
My table and the data.
You are not adding your loaded List<Taxon> to the model.
#RequestMapping(value="/showTaxons")
public String homePage(Model model){
List<Taxon> taxons = taxonService.findAll();
return "index";
}
Just returns the page to render, without modifying the model.
So this should work
#RequestMapping(value="/showTaxons")
public String homePage(Model model){
model.add(taxonService.findAll());
return "index";
}
In the end I added a few more anotations
#Data
#NoArgsConstructor
#AllArgsConstructor
#Validated
#Entity
#Table(name = "table name")
And explicit mapping for columns
#Column(name = "column_name")
this helped

How can I share an Entity for other entities' OneToMany

I have an Entity look like this.
#Entity
class Property extends BaseEntity {
#Basic
private String name;
#Basic
private String value;
}
The basic intention is using this Entity as other Entities properties.
#Entity
class MyEntity extends BaseEntity {
#OneToMany
private List<Property> properties;
}
#Entity
class YourEntity extends BaseEntity {
#OneToMany
private List<Property> properties;
}
How can I do this? Do I have to define each owner's field in Property?
#Entity
class Property extends BaseEntity {
#Basic
private String name;
#Basic
private String value;
#ManyToOne(optional = true)
private MyEntity myEntity;
#ManyToOne(optional = true)
private YourEntity yourEntity;
#ManyToOne(optional = true)
private OtherEntity otherEntity;
}
Basically it is a good solution You represented here. There is the option to create a join table which will help you to keep the entity "cleaner" (and also could be used as a ManyToMany. In most of the cases I prefer to use the option You provided [simplicity is a gooooood thing :) ], but other colleagues got different view on this problem.
TL.DR: Your provided code is working and I personally prefer it. There are other ways but those are a bit slower etc.

Accessing #MappedSuperclass' properties in Criteria Query using metamodel classes

I'm trying to access a property defined in #MappedSuperclass in a Criteria Query with JPA meta-model (Hibernate 4.2.8):
#MappedSuperclass
public class BaseEntity {
private DateTime createdOn;
}
My Entity:
#Entity
#Table(name = "HISTORY")
public class History extends BaseEntity implements Serializable {
private Long id;
}
How to access createdOn?
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<History> c = cb.createQuery(History.class);
Root<History> h = c.from(History.class);
h.orderBy(cb.asc(a.get(History_.createdOn)));
History_:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(History.class)
public abstract class History_ extends com.test.BaseEntity_ {
public static volatile SingularAttribute<History, Long> id;
}
Is there a way to access base class' properties?
Of course there is a way. As I see in your code, you already access it: History_.createdOn is a property of the MappedSuperclass. If by "to access" you mean to select, than you simply do:
query.select(History_.createdOn)