Can't access a property of a Embedded class via JPA - spring-boot

#Entity
#EntityListeners(AuditingEntityListener.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TIPO_CONTRATO", discriminatorType = DiscriminatorType.STRING)
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Contrato extends AuditorEntity implements Serializable, Clonable {
#Column(name = "CIF_NIF")
#JsonView(Views.Buscador.class)
#JsonProperty("cifNif")
private String cifNif;
#Column(name = "NOMBRE_SOCIEDAD_PERSONA")
#JsonView(Views.Buscador.class)
private String nombreSociedadPersona;
}
And i have this Embeddable class called CuentaBancaria from Contrato table:
#Embeddable
public class CuentaBancaria implements Serializable {
private static final long serialVersionUID = 6835775213299596371L;
#Column(name = "TITULAR_CUENTA")
#JsonView(Views.Completo.class)
private String titularCuenta;
}
In ContratoRepository i'm trying doing a JPA Query finding the "titularCuenta" field of Cuenta Bancaria finding by the cifNif field of Contrato. But it's not working. What can i do to solve this?
#Query(value="SELECT c.CuentaBancaria.titularCuenta FROM Contrato c WHERE c.cifNif= ?1 AND c.nombreSociedadPersona IS NOT NULL AND ROWNUM = 1")
public String getNombreLegalCliente(String cifNif);
The error which is throwing:
Caused by: org.hibernate.QueryException: could not resolve property:
CuentaBancaria of: com.xxxx.Contrato

You're missing CuentaBancaria field in Contrato class. That's why JQL complains.
Add the field in the class with #Embedded annotation:
public class Contrato extends AuditorEntity implements Serializable, Clonable {
#Embedded
private CuentaBancaria cuentaBancaria;
}
And fix the JQL expression to:
#Query(value="SELECT c.cuentaBancaria.titularCuenta FROM Contrato c WHERE c.cifNif= ?1 AND c.nombreSociedadPersona IS NOT NULL AND ROWNUM = 1")
public String getNombreLegalCliente(String cifNif);

Yes, since your class [ CuentaBancaria ] is annotated with #Embeddable, it needs to be embedded in the parent class in this case [ Contrato ] with #Embedded.
Then, harnessing Spring Data JPA query Lookup strategies, you can access property fields of your embedded class with ease or you could still go by the #Query() approach
Query lookup Strategy from Spring documentation
Sample demo code with your problem with a minimal implementation:
Entity-Class
--------------
#Entity
public class Contrato{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long contratoId;
#Column(name = "CIF_NIF")
private String cifNif;
#Column(name = "NOMBRE_SOCIEDAD_PERSONA")
private String nombreSociedadPersona;
//we call the embeddable class in this parent class with #Embedded annotation
#Embedded
private CuentaBancaria cuentaBancaria
}
Embeddable-Class
-----------------
#Embeddable
public class CuentaBancaria{
#Column(name = "TITULAR_CUENTA")
private String titularCuenta;
}
Now in your ContratoRepository class, we could have
#Repository
public interface ContratoRepository extends CrudRepository<Contrato, Long> {
Optional<Contrato> findByCuentaBancariaTitularCuenta(String cifNif);
}
which interprets to JPQL snippet:
c.cuentaBancaria.titularCuenta FROM Contrato c WHERE c.cifNif= ?1
NOTE: Notice the query method name matches the exact names in the classes and their corresponding fields, preceded by findBy

Related

JPARepository CPRQ modified does not save full object

I have modified the design of CPRQ a bit to help my database pattern
I have an Employee table and a Department table. Both have common properties
#Column(name="tenantIDPKFK")
private Integer tenantIdpkfk;
#Column(name="status")
private Integer status;
So I created a base class ABaseEntity like below
public class ABaseEntity {
public ABaseEntity() {
}
public ABaseEntity(int tenantIdpkfk, int status) {
this.tenantIdpkfk = tenantIdpkfk ;
this.status = status ;
}
#Column(name="tenantIDPKFK")
private Integer tenantIdpkfk;
#Column(name="status")
private Integer status;
I have extended EmployeeEntity with ABaseEntity
#Entity
#Table(name = "employee")
public class EmployeeEntity extends ABaseEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "first_name")
#NotEmpty(message = "Please provide a name")
#NotBlank
private String firstName;
My CommandHandler runs the following code
EmployeeEntity savedEmployeeEntity = this.employeeRepository.saveAndFlush(employee);
this.mediator.emit(new EmployeeCreatedEvent(savedEmployeeEntity.getId()));
Database saved the object, but only id, firstname. Does not save tenant and status columns.
I know I am missing something silly. Please help.
EDIT
Adding #MappedSuperclass to the ABaseEntity class fixed the issue.
#MappedSuperclass
public class ABaseEntity {...}
Database saved the object, but only id, firstname. Does not save
tenant and status columns.
By default JPA doesn't consider the parent class in the orm (object-relational mapping) of the current class.
You have to specify on the parent class #Inheritance with the strategy to use or use the default one.
For example :
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class ABaseEntity {...}
More info here.

Spring - JPA join abstract class in abstract class

I have a problem with JPA inheritance. The database model is also specially built. It contains several tables with the same attributes (the tables were intentionally cut by country) and all these tables connect to another table (OneToOne).
Here is an example of the data model:
usa_user, germany_user, austria_user. All these tables have the same attributes (id, name, address). Now the address was also built up according to the countries e.g. usa_address, germany_address, austria_address.
Now I don't know or have the problem that I have been mapping them correctly for a long time. I have the following:
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", referencedColumnName = "id")
#JsonIgnore
private User user;
private String name;
private String addr_num;
...
}
// All Lombok Getter, Setter Args,...
#MappedSuperclass
public abstract User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JsonIgnore
private Address address;
private String name;
}
#Entity
#Table(name = "usa_user")
public class UsaUser extends User {}
#Entity
#Table(name = "austria_user")
public class AustriaUser extends User {}
#Entity
#Table(name = "germany_user")
public class GermanyUser extends User {}
#Entity
#Table(name = "usa_address")
public class UsaAddress extends Address {}
#Entity
#Table(name = "austria_address")
public class AustriaAddress extends Address {}
#Entity
#Table(name = "germany_address")
public class GermanyAddress extends Address {}
But unfortunately this does not work. Every time I start it JPA notices that it can't map the Entities Address - User (which is understandable because they are not entities but abstract classes). What would be the best way to solve this? I want to avoid that I have to list the attributes in all these entities because it would be redundant.
The goal is to find out how I can use a #MappedSuperclass in a #MappedSuperclass.
MappedSuperclass is not queryable and thus also not joinable. You need to map this as an abstract entity with the table per class inheritance strategy. Just switch to #Entity on the Address and User and add #Inheritance(TABLE_PER_CLASS).

How to use #NamedEntityGraph with #EmbeddedId?

I'm trying to have Spring Data JPA issue one query using joins to eagerly get a graph of entities:
#Entity
#NamedEntityGraph(name = "PositionKey.all",
attributeNodes = {#NamedAttributeNode("positionKey.account"),
#NamedAttributeNode("positionKey.product")
})
#Data
public class Position {
#EmbeddedId
private PositionKey positionKey;
}
#Embeddable
#Data
public class PositionKey implements Serializable {
#ManyToOne
#JoinColumn(name = "accountId")
private Account account;
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
}
Here's my Spring Data repo:
public interface PositionRepository extends JpaRepository<Position, PositionKey> {
#EntityGraph(value = "PositionKey.all", type = EntityGraphType.LOAD)
List<Position> findByPositionKeyAccountIn(Set<Account> accounts);
}
This produces the following exception:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [positionKey.account] on this ManagedType
I want all of the accounts and products to be retrieved in one join statement with the positions. How can I do this / reference the embedded ID properties?
I would suggest refactoring the entity this way if it possible
#Entity
#NamedEntityGraph(name = "PositionKey.all",
attributeNodes = {#NamedAttributeNode("account"),
#NamedAttributeNode("product")
})
#Data
public class Position {
#EmbeddedId
private PositionKey positionKey;
#MapsId("accountId")
#ManyToOne
#JoinColumn(name = "accountId")
private Account account;
#MapsId("productId")
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
}
#Embeddable
#Data
public class PositionKey implements Serializable {
#Column(name = "accountId")
private Long accountId;
#Column(name = "productId")
private Long productId;
}
Such an EmbeddedId is much easier to use. For instance, when you are trying to get an entity by id, you do not need to create a complex key containing two entities.

Ignore column if not extist in table Spring Data JPA

I have an abtract class like below:
#MappedSuperclass
public abstract class BaseEntity {
#Id
Long id;
String name;
//getters and setters
}
and two entity extend BaseEntity
Fist class
#Entity
#Table(name= "table1")
public class TValideB extends BaseEntity {
#Column(name = "phone")
String phone;
}
Second class
#Entity
#Table(name= "table2")
public class TValide extends BaseEntity {
#Colmun(name = "mail")
String mail;
}
When i try to save TValide i get error like this non valid column "name";
In my table2 non column exsit for name.
My question is how can i ignore this column and save my entity?
Exist an other approaches without delete column name from abstract
class?
If you must use base classes, you can create two.
#MappedSuperclass
public abstract class BaseEntityWithId {
#Id
Long id;
//getters and setters
}
#MappedSuperclass
public abstract class BaseEntityWithName extends BaseEntityWithId {
String name;
//getters and setters
}
Then you simply have to pick the right one depending on the column layout of the table.
I just add #Column in name with insertable=false, updatable=false edit my abstract class like below:
#MappedSuperclass
public abstract class BaseEntity {
#Id
Long id;
#Column(name = "name", insertable=false, updatable=false)
String name;
//getters and setters
}
This approach avoid me to create a lot of abstract class or rewrite override attributes.
This should solve your problem.
In your class that extends BaseEntity, do this for the column that you don't want.
#Entity
#AttributeOverride(name = "name", column = #Column(name = "name", insertable = false, updatable = false)
public class TValide extends BaseEntity {
#Colmun(name = "mail")
String mail;
}
Change the way you use abstraction. Create a new abstract class that extends BaseEntity contains fields that are rarely used.
Example:
#MappedSuperclass
public abstract class BaseEntity {
#Id
Long id;
//getters and setters
}
#MappedSuperclass
public abstract class BaseNameEntity extends BaseEntity {
String name;
//getters and setters
}
#Entity
#Table(name= "table1")
public class TValideB extends BaseNameEntity {
#Column(name = "phone")
String phone;
//getters and setters
}
#Entity
#Table(name= "table2")
public class TValide extends BaseEntity {
#Column(name = "mail")
String mail;
//getters and setters
}
By doing this you can configure all your structure.

SpringData and Elasticsearch - java.lang.StackOverflowError while using ignoreFields

I am using simple relationship (spring data elasticsearch) but springboot is throwing StackOverflowError
#Document(indexName = "users", type = "user")
public class User {
#Id
private String id;
#Field(type= FieldType.Nested,ignoreFields={"users"})
private Set<Group> groups = new HashSet<Group>();
}
#Document(indexName = "groups", type = "group")
public class Group {
#Id
String id;
#Field(type = FieldType.Nested, ignoreFields ={"groups"})
private Set<User> users = new HashSet<User>();
}
public interface UserRepository extends ElasticsearchRepository<User, String>{
}
public interface GroupRepository extends ElasticsearchRepository<Group, String> {
}
Any Idea what is problem?
Code is from https://github.com/spring-projects/spring-data-elasticsearch/tree/master/src/test/java/org/springframework/data/elasticsearch/entities
Thanks
Rajan
As you said: spring-boot is throwing StackOverflowError.
It's because of no safeguards or detecting circular dependencies in spring-boot.
Look at spring-boot source: MappingBuilder.java, there's loop that will never break when you have class A that have a #Field annotation referencing class B, that have #Field annotation referencing class A.

Resources