JPA Join Query using #Query in repository - java

I have two tables with composite primary keys and also it has foreign key relation ship. I am trying JPA mappings between these tables. We need to get the below join query result from JPA repository
select * from A i inner join B n on i.id = n.id where i.id = 'XXX' and i.version=99999;
Table: A fields are
id, name, version are primary keys
Table: B fields are
id, name, version, type are primary keys id, name, version, are foreign keys
#Table(name = "A")
#IdClass(APK.class)
public class A {
#Id
#Column(name = "ID")
private String id;
#Id
#Column(name = "NAME")
private String name;
#Id
#Column(name = "Version")
private String version;
//getter setter, toString, equals and hash code
#OneToOne #JoinColumn(name="ID")
private B b;
getter setter
}
public class APK implements Serializable {
private String id;
private Long version;
private String name;
//getter setter, toString, equals and hash code
}
#Table(name = "B")
#IdClass(BPK.class)
public class B {
#Id
#Column(name = "ID")
private String id;
#Id
#Column(name = "NAME")
private String name;
#Id
#Column(name = "Version")
private String version;
#Id
#Column(name = "TYPE")
private Type type;
// getter setter, toString, equals and hash code
#OneToOne #MapsId("ID")
private A a;
//getter setter
}
public class APK implements Serializable {
private String id;
private Long version;
private String name;
private Type type;
//getter setter, toString, equals and hash code
}
Getting the below error with this code:
Caused by: org.hibernate.AnnotationException: A Foreign key refering
com.domain.data.B from com.domain.data.A has the wrong number of
column. should be 5
Please Help on this.

The issue is resolved by using below mapping
#Table(name = "A")
#IdClass(APK.class)
public class A {
#OneToMany(fetch=FetchType.LAZY, mappedBy="a")
private List<B> b;
//getter and setter for b
}
#Table(name = "B")
#IdClass(BPK.class)
public class B {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "id", insertable = false, updatable = false),
#JoinColumn(name = "name", insertable = false, updatable = false),
#JoinColumn(name = "version", insertable = false, updatable = false)
})
private A a;
//getter and setter for a
}
The JPQL query is "select i from B i INNER JOIN i.a n WHERE n.id=:id"

Related

Hibernate JoinFormula partial PK EmbeddedId

I have a table that holds events where the data is stored as a JSON object.
What i'am trying to do is have different entities mapped to the same table, because some of these events need to join with other tables to be useful.
What i tried doing:
EntityA is the "default" entity for the table, all events are received in this entity an them stored.
#Entity(name = "entity_a")
#Table(name = "table_a")
public class EntityA {
#Id
private UUID id;
#Column(name = "fielda")
private String fieldA;
#Column(name = "fieldb")
private Integer fieldB;
#Column(name = "json_field")
private Object jsonField;
}
EntityAB is a map of the same table with a new field that is a join with another table.
#Entity(name = "entity_ab")
#Table(name = "table_a")
public class EntityAB {
#Id
private UUID id;
#Column(name = "fielda")
private String fieldA;
#Column(name = "fieldb")
private Integer fieldB;
#Column(name = "json_field")
private Object jsonField;
#ManyToOne
#ManyToOne
#JoinColumnsOrFormulas(value = {
#JoinColumnOrFormula(formula = #JoinFormula(value = "JSON_VALUE(jsonField, '$.field_id_a')", referencedColumnName = "field_id_a")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "JSON_VALUE(jsonField, '$.field_id_b')", referencedColumnName = "field_id_b")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "JSON_VALUE(jsonField, '$.fieldc')", referencedColumnName = "fieldc")),
})
private EntityB entityB;
}
EntityB is used in the EntityAB.
public class EntityB implements Serializable {
#EmbeddedId
private EntityBId id;
#Column(name = "fieldc")
private String fieldC;
}
#Embeddable
public class EntityBId implements Serializable {
#Column(name = "field_id_a")
private String fieldIdA;
#Column(name = "field_id_b")
private Integer fieldIdB;
#Column(name = "field_id_c")
private Integer fieldIdC;
}
When starting the application, it gives me this error:
referencedColumnNames(field_id_a, field_id_b, fieldc) of EntityAB.entityB referencing EntityB not mapped to a single property
If i dont use the fields "field_id_a" and "field_id_b" (EmbeddedId) in the join, it works, but it's not the right join. From what i have searched it looks like i can't have a parcial PK join when using "EmbeddedId", is that correct?

How to join column with multiple primary keys or specific primary key in jpa

#Entity
#Table(name = "A")
public class A {
#Id
#Column(name = "id")
private Long id;
#Id
#Column(name = "name")
private String name;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
#Entity
#Table(name = "Test")
public class Test {
#Id
#Column(name = "test_id")
private Long testId;
}
Error Result is
" JPA trouble with OneToOne relationship: A Foreign key refering has the wrong number of column. should be 2 "
How to specific primary key for join Test table ?
Table A : column id
map with
Table B : column test_id
Since your table A has a composite key, you should separate the columns out into another key class and then join on the individual part of the key of the table.
For instance, create AKey
#Embeddable
public class AKey {
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
//getters and setters
}
Then replace the ids in class A
#Entity
#Table(name = "A")
public class A {
#EmbeddedId
private AKey key;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
Then you can do a join on Test.testId = A.key.id

How can I convert this 3 JOIN query into a Spring Data JPA named query method?

I am not so into Spring Data JPA and I have the following problem trying to implement a named query (the query defined by the method name).
I have these 3 entity classes:
#Entity
#Table(name = "room_tipology")
public class RoomTipology implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "tipology_name")
private String name;
#Column(name = "tipology_description")
private String description;
#Column(name = "time_stamp")
private Date timeStamp;
#OneToMany(mappedBy = "roomTipology")
private List<Room> rooms;
#OneToOne(mappedBy = "roomTipology")
private RoomRate roomRate;
// GETTER AND SETTER METHODS
}
That represents a tipology of room and that contains this field
#OneToMany(mappedBy = "roomTipology")
private List<Room> rooms;
So it contains the list of room associated to a specific room tipology, so I have this Room entity class:
#Entity
#Table(name = "room")
public class Room implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "id_accomodation_fk", nullable = false)
private Accomodation accomodation;
#ManyToOne
#JoinColumn(name = "id_room_tipology_fk", nullable = false)
private RoomTipology roomTipology;
#Column(name = "room_number")
private String number;
#Column(name = "room_name")
private String name;
#Column(name = "room_description")
#Type(type="text")
private String description;
#Column(name = "max_people")
private Integer maxPeople;
#Column(name = "is_enabled")
private Boolean isEnabled;
// GETTER AND SETTER METHODS
}
Representing a room of an accomodation, it contains this annoted field:
#ManyToOne
#JoinColumn(name = "id_accomodation_fk", nullable = false)
private Accomodation accomodation;
And finally the Accomodation entity class:
#Entity
#Table(name = "accomodation")
public class Accomodation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy = "accomodation")
private List<Room> rooms;
#Column(name = "accomodation_name")
private String name;
#Column(name = "description")
#Type(type="text")
private String description;
// GETTER AND SETTER METHODS
}
Ok, so now I have this Spring Data JPA repository class for RoomTipology:
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface RoomTipologyDAO extends JpaRepository<RoomTipology, Long> {
}
Here I want to define a named query method that return to me the list of all the RoomTipology object related to a specific accomodation, I have done it using SQL and it works fine:
SELECT *
FROM room_tipology as rt
JOIN room r
ON rt.id = r.id_room_tipology_fk
JOIN accomodation a
ON r.id_accomodation_fk = a.id
WHERE a.id = 7
But now I want to translate it in a named query method (or at least using HQL)
How can I do it?
Please Try:
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface RoomTipologyDAO extends JpaRepository<RoomTipology, Long> {
List<RoomTipology> findByRooms_Accomodation(Accomodation accomodation);
}
The query builder mechanism built into Spring Data repository infrastructure is useful for building constraining queries over entities of the repository. The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it
At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties.
Doc:Here

Incompatible data types while ALTER table in HSQL with Hibernate

How fix next error?
ERROR SchemaExport:484 - HHH000389: Unsuccessful: alter table CATEGORY_RELATIONS add constraint FK2bn4xlg661b5xbx2qnwi1aqv0 foreign key (CATEGORY_RELATIONS_PARENT_ID) references CATEGORY
ERROR SchemaExport:485 - incompatible data types in combination in statement [alter table CATEGORY_RELATIONS add constraint FK2bn4xlg661b5xbx2qnwi1aqv0 foreign key (CATEGORY_RELATIONS_PARENT_ID) references CATEGORY]
hibernate.version 5.0.7.Final
hsqldb.version 2.3.3
Property, used for session factory
hibernate.dialect=org.hibernate.dialect.HSQLDialect
Category
#Entity
#Table(name = "CATEGORY",
indexes = {
#Index(name = "CATEGORY_NAME_INDEX",
columnList = "CATEGORY_NAME")})
public class Category extends JsonNamedModel {
#Id
#Column(name = "CATEGORY_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "CATEGORY_NAME")
private String name;
#Column(name = "CATEGORY_IMAGE")
private String image;
#OneToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
#JoinTable(name = "CATEGORY_RELATIONS",
joinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_CATEGORY_ID", referencedColumnName = "CATEGORY_ID")},
inverseJoinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_PARENT_ID", referencedColumnName = "CATEGORY_ID")})
private Category parent;
#OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "parent")
private List<Category> children;//...
}
CategoryRelations
#Entity
#Table(name = "CATEGORY_RELATIONS")
#IdClass(CategoryRelations.CategoryRelationsPrimaryKey.class)
public class CategoryRelations implements Serializable {
#Id
#Column(name = "CATEGORY_RELATIONS_CATEGORY_ID")
private String categoryId;
#Id
#Column(name = "CATEGORY_RELATIONS_PARENT_ID")
private String parentId;
#Entity
#IdClass(CategoryRelationsPrimaryKey.class)
public static class CategoryRelationsPrimaryKey implements Serializable {
private long categoryId;
private long parentId;
}//...
}
I think it's complaining because your types don't match up. In CATEGORY_RELATIONS you have the key types as String but in CATEGORY you have the primary key as an int. While in actual practice you might only store integer data in both fields, the DB engine can't prove that. There's nothing stopping somebody from putting a non-integer in CATEGORY_RELATIONS.categoryId and making it so the FK could never be satisfied.
Try changing CATEGORY_RELATIONS.categoryId to an int.
And now that I look at it, your PK class shows them as longs. Try switching all of the types in your CategoryRelations object (and possibly the CATEGORY_RELATIONS table) to all be the same types.

eclipselink SQLServerException inserting into a join table with additional column

I have a many-to-many relationship which looks like this:
The primary key is combination of three columns and I'm using eclipselink. I created these classes to be able to insert in the join-table :
#Entity
#Table(name = "definition_property")
#NamedQueries({
#NamedQuery(name = "DefinitionProperty.findAll", query = "SELECT d FROM DefinitionProperty d")})
public class DefinitionProperty extends AbstractEntity{
private static final long serialVersionUID = 1L;
#EmbeddedId
protected DefinitionPropertyPK pk;
#JoinColumn(name = "dtid", referencedColumnName = "id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private DefinitionType definitionType;
#JoinColumn(name = "prid", referencedColumnName = "id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Property property;
#Column(name = "initial_value")
#Basic(optional = false)
private String initialValue;
// setters and getters
}
And PK class:
#Embeddable
public class DefinitionPropertyPK implements Serializable{
#Column(name = "dtid")
private Integer idDt;
#Column(name = "prid")
private Integer idProperty;
#Column(name = "initial_value")
private String initialValue;
//hashcode, equals, setters and getters
}
Entitiy 1:
#Entity
#Table(name = "definition_type")
public class DefinitionType extends AbstractEntity implements EntityItem<Integer> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer idDT;
#Size(max = 45)
#Column(name = "name")
private String dtName;
#Size(max = 45)
#Column(name = "description")
private String description;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "definitionType")
private List<DefinitionProperty> definitionProperties = new ArrayList<DefinitionProperty>();
}
Entity 2:
#Entity
#Table(name = "property")
public class Property extends AbstractEntity implements EntityItem<Integer>{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
#Basic(optional = false )
#Column(name = "id")
private Integer idProperty;
#Column(name = "name")
#Size(max = 45)
private String propertyName;
#Enumerated(EnumType.STRING)
#Column(name = "type")
private Type fieldType;
#Column(name = "init_value")
#Size(max = 45)
private String initialValue;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "property")
private List<DefinitionProperty> definitionPeoperties= new ArrayList<DefinitionProperty>();
}
Exception : I get this exception when trying to persist a new DefinitionType:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: The column name 'initial_value' is specified more than once in the SET clause. A column cannot be assigned more than one value in the same SET clause. Modify the SET clause to make sure that a column is updated only once. If the SET clause updates columns of a view, then the column name 'initial_value' may appear twice in the view definition.
Error Code: 264
Call: INSERT INTO definition_property (initial_value, initial_value, dtid, prid) VALUES (?, ?, ?, ?)
bind => [4 parameters bound]
Question : Why there are two initial_values in the set clause and where am I wrong in my code?
Ref: How do you Set Up a Many-to-Many Relationship with Junction Table using JPA/EclipseLink
Ref: JPA simple many-to-many with eclipselink
Ref: https://giannigar.wordpress.com/2009/09/04/mapping-a-many-to-many-join-table-with-extra-column-using-jpa/

Categories