It`s possible to create one map with hibernate #ManyToOne just like this:
public class IndicadorAtos {
#JsonIgnore
#Id
#Column(name="cod_ato_praticado")
private Integer codAtoPraticado;
#Column(name="descricao_ato")
private String ato;
#JoinColumn(name = "cod_ato", referencedColumnName = "cod_ato")
#ManyToOne
#Fetch(FetchMode.SUBSELECT)
private Atos atos;
}
But in some cases I dont have association or in my table IndicadorAtos have one code, that don`t existis in table Atos
this is my tables:
create table IndicadorAtos (
codAtoPraticado integer primary key,
ato varchar(250),
cod_ato integer
);
create table Atos(
cod_ato integer primary key.
name varchar(250)
)
I try to create this join:
Select t FROM IndicadorAtos t , Atos a where t.cod_ato = a.cod_ato, but I need to return all records from my IndicadorAtos, and with this select he only return all itens that have one item in Atos.
tks
It`s possible to create one map with hibernate #ManyToOne
Yes; it is called unidirectional relationship.
If I understood your question properly, you want to select all entries from IndicadorAtos with possibly associated entries from Atos. You can achieve this by using left join as follows:
SELECT t FROM IndicadorAtos t LEFT JOIN t.atos at
provided that you have an entity Atos defined like:
#Entity
public class Atos {
#Id #GeneratedValue
private int cod_ato;
private String name;
// getters and setters
}
Related
I have two tables that already exist inside postgres, lets call the Table A and Table B. One column of Table B has a foreign key constraint in that it has to be the primary key of Table A. Thus there is a many-to-one relationship between B and A, where multiple records in Table B correspond to one record of Table A.
The Entity for both these tables are defined as follows.
public class TableA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
#Column(name = "phone_number")
private String phoneNumber;
}
TableB's entity is defined as follows:
public class Shots implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long itemId;
#Column(name = "user_id")
private Long userId;
}
Where userId is the foreign key mapping to the primary key user_id in Table A.
These constraints have already been defined in the underlying postgres database, so i didn't consider using the #ManyToOne annotation relationship (still trying to wrap my head around it).
The way i currently handle the case when a foreign key constraint violation occurs is by doing the following:
try {
tableBrepository.save(newShot);
} catch (ConstraintViolationException ex) {
logger.error("Violating foreign key constraint" + ex.getMessage());
}
My question is, is there a better way to check for this violation? Is there anything i can do to generally better structure the foreign key constraint in Spring Data JPA?
Thus there is a many-to-one relationship between B and A, where multiple records in Table B correspond to one record of Table A.
This kind of stuff in JPA entities is handled with #ManyToOne annotation. You usually do not refer to any id field directly but tell JPA what there should be. So in your class TableB (or should I call it... Shots?) should be something like:
#ManyToOne
private TableA tableA;
// and get rid of this
// #Column(name = "user_id")
// private Long userId;
And optionally - so not necessarily - you could have, in your TableA:
#OneToMany
private List<TableB> tableBsOrShouldICallYouShots;
I am not sure what is your actual problem but when setting and referring to id fields directly might cause your difficulties.
Now if you -for example- use repository to find some TableB you can then after that just do
tableB.getTableA()
And when saving you would before that do:
tableB.setTableA(somSortOftableA);
// so not tableB.setUserId(someLongIdFOrtableA);
Now the point is that there is no problem with referential integrity because you do not need to know any IDs and you cannot set any wrong ID. Unless you first need to fetch TableA by id before setting it to TableB but in that case you would still not set any IDs.
I have two tables, Projects and TransitionAction. Both Projects and TransitionAction have a column request_no which is used perform join between them. The entity classes are as below:-
Project.java
#Entity
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(schema = "public", name="project_id_seq_gen",sequenceName="project_id_seq",initialValue=1,allocationSize=1)
#GeneratedValue(strategy= GenerationType.SEQUENCE,generator="project_id_seq_gen")
private Integer id;
#Column(name = "request_no")
private String request_no;
#Column(name = "title")
private String title;
#Column(name = "department")
private String department;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "requestNo")
private Set<TransitionAction> tacts;
#ManyToOne
#JoinColumn(name = "status_id")
private Status status;
#ManyToOne
#JoinColumn(name = "level_id")
private ProjectLevel level;
TransitionAction.java
#Entity
#Table(name = "transitionaction")
public class TransitionAction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "request_no")
private String request_no;
#Column(name = "actionDate")
private Date actionDate;
The code used to retrieve the list of projects as follows:-
public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {
UserInfo findByUserName(String userName);
#Query("SELECT project FROM Project project Join project.tacts pta where project.request_no= pta.request_no and project.status.id=1")
List<Project> getAllUserProjects();
}
I am getting could not extract ResultSet error. When I checked console, i found the following query being generated:
select
distinct project0_.id as id1_1_,
project0_.department as departme2_1_,
project0_.level_id as level_id6_1_,
project0_.user_nodal_officer as user_nod3_1_,
project0_.request_no as request_4_1_,
project0_.status_id as status_i7_1_,
project0_.title as title5_1_
from
project project0_
inner join
transitionaction tacts1_
on project0_.id=tacts1_.request_no
I am not getting why project. id is joined with tact.request_no and creating the error
Operator does not exist: integer = character varying
So you want to get all TransitionAction linked to Project by requestNo
You can achieve this by
adding #ManyToOne Mapping in TransactionAction like this
#ManyToOne(fetch = FetchType.LAZY)
private Project project;
Now you need to modify your query like this
#Query("SELECT project FROM Project project where project.status.id=1")
List<Project> getAllUserProjects();
To get all TransactionAction for a given project
Set<TransitionAction> allTatcts = project.getTacts();
You dont need to add join in query. Hibernate will take care of that once you pull TransactionAction from Project by an entity.
Edit 1:
But why did my query fail ? Why primary key of project joined with
request_no of TransitionAction ?
Your query failed because while doing #OneTOMany relation yodidn'tnt define #JoinColumn or #JoinTable which is a unidirectional mapping.
In this case, Hibernate will use primarykey column to map.
And since type of primarykey and column is different thus the error.
Without describing any physical mapping (no #JoinColumn or
#JoinTable), a unidirectional one to many with join table is used. The
table name is the concatenation of the owner table name, _, and the
other side table name. The foreign key name(s) referencing the owner
table is the concatenation of the owner table, _, and the owner
primary key column(s) name. The foreign key name(s) referencing the
other side is the concatenation of the owner property name, _, and the
other side primary key column(s) name. A unique constraint is added to
the foreign key referencing the other side table to reflect the one to
many.
Refer Official doc for more detail
#OneToMany
#JoinTable(
name="table_name",
joinColumns = #JoinColumn( name="columnname")
)
private Set<TransitionAction> tacts;
It is because you are having #Id on request_no in your TransactionAction class and when you join two class then they join on primary keys.
You can use cross join instead.
SELECT project FROM Project project, TransactionAction pta where project.request_no= pta.request_no and project.status.id=1
Or you can have bidirectional mapping. Add the following in your TransactionAction entity class.
#ManyToOne
private Project Project;
And then your query will be as follows.
select pta.project from TransactionAction pta where pta.request_no=pta.project.request_no and pta.project.status.id=1
I have an entity which has map list with sub enitities. When I want to delete this entity , I've got
Cannot delete or update a parent row: a foreign key constraint fails (kobalt.category_categoryitem,
error and I use hibernate. Is there anyone who can solve this problem ?
#Entity
public class Product implements BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private int id;
private int kobilId;
private String code;
#Column(unique = true)
private long barcode;
private String productName;
#Column
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#Cascade(org.hibernate.annotations.CascadeType.DELETE)
private Map<Category, CategoryItem> itemList;
Looks like relationship structure is not correct in your case. When you are trying to delete foreign key corresponding value it can't delete primary key corresponding value. In cascade rule relationship should be like as when you delete primary key corresponding attribute then it should delete all foreign key corresponding attributes. But can't be vice-versa. That is your case i think so try to correct your relationships. For example consider "employee - address" relationship where employee has array of address. So relationship should be like as when employee is deleted, all the corresponding address should vanish. But on the other hand if relationship is not correct and you try to delete an address then it can't be possible to delete employee, as a result you'll get such exception as in your case.
i am trying on many to many relationship, Team member can work on multiple projects and a project can have multiple team member , the table structure is as follows,
create table TBL_PROJECT_ONE(
id integer primary key generated always as identity(start with 12,increment by 3),
name varchar(50)
)
create table TBL_TEAM_MEMBER_ONE(
id integer primary key generated always as identity(start with 7,increment by 5),
name varchar(50),
salary integer
)
create table EMP_PRJ_CADRE(
MEMBER_ID integer references TBL_TEAM_MEMBER_ONE,
PRJ_ID integer references TBL_PROJECT_ONE,
CADRE varchar(10),
constraint PK_001_EMP_TEAM primary key (MEMBER_ID,PRJ_ID)
)
Here i have created a new table just to store the relationship,
Now please follow the Employee entity,
#Entity
#Table(name="TBL_TEAM_MEMBER_ONE")
public class EmployeeEntityFour implements Serializable{
public EmployeeEntityFour(){}
public EmployeeEntityFour(String empName,Integer salary){
...
..
}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="ID")
private Integer empId;
#Column(name="NAME")
private String empName;
#Column(name="SALARY")
private Integer empSal;
#ElementCollection(fetch= FetchType.LAZY)
#CollectionTable(name="EMP_PRJ_CADRE")
#MapKeyJoinColumn(name="PRJ_ID")
#Column(name="CADRE")
private Map<ProjectEntityOne,String> employeeCadre;
...
..
.
}
Please follow the mapping for Project Entity,
#Entity
#Table(name="TBL_PROJECT_ONE")
public class ProjectEntityOne implements Serializable{
public ProjectEntityOne(){}
public ProjectEntityOne(String name){
this.projectName = name;
}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="ID")
private Integer projectId;
#Column(name="NAME")
private String projectName;
#ElementCollection(fetch= FetchType.LAZY)
#CollectionTable(name="EMP_PRJ_CADRE")
#MapKeyJoinColumn(name="MEMBER_ID")
#Column(name="CADRE")
private Map<EmployeeEntityFour,String> employeeCadre;
....
..
.
}
In main method testing the code written is as follows,
ProjectEntityOne proj = new ProjectEntityOne("Citi Grand Central");
Map<EmployeeEntityFour,String> cadreMap = new HashMap<EmployeeEntityFour,String>();
cadreMap.put(new EmployeeEntityFour("Murlinarayan Muthu",34000), "Senior Software Engineer");
cadreMap.put(new EmployeeEntityFour("Gopalkrishna Rajnathan",64000), "Software Engineer");
cadreMap.put(new EmployeeEntityFour("Premanna Swaminathan",94000), "Project Manager");
proj.setEmployeeCadre(cadreMap);
em.persist(proj);
but i am getting an error which is
ERROR: 'PROJECTENTITYONE_ID' is not a column in table or VTI 'APP.EMP_PRJ_CADRE'.
When in both the entities i have specified #MapKeyJoinColumn than too i am getting an error as improper column for the third table.
Where i am missing
It somehow worked, i had to do some changes in the code,
first, the edited code in Entity ProjectEntityOne is as follows,
#ElementCollection(fetch= FetchType.LAZY)
#CollectionTable(name="EMP_PRJ_CADRE",joinColumns=#JoinColumn(name="PRJ_ID"))
#MapKeyJoinColumn(name="MEMBER_ID")
#Column(name="CADRE")
private Map<EmployeeEntityFour,String> employeeCadre;
What i have done here is i added #JoinedColumn in #CollectionTable,
Second change i did in Entity EmployeeEntityFour, the change is I removed Map of PorjectEntityOne from it,
in test,
i can save Project with Employee mapping but here all the employees should be already saved one.
i.e. the key of map
Map<EmployeeEntityFour,String> employeeCadre;
should be already persisted
and than we can persist project entity.
On employeeCadre in EmployeeEntityFour you need a #JoinColumn(name="MEMBER_ID") and you would also need a #JoinColumn(name="PRJ_ID") in the ProjectEntityOne employeeCadre.
But, I would not model it this way. First of all you cannot have a bi-directional ElementCollection mapping, and ElementCollection can only be owned by one side. The best solution would be to define an Cadre entity mapping to EMP_PRJ_CADRE table and have a OneToMany to it from both sides, and have it have a ManyToOne to each.
Alternatively you may use a ManyToMany with a MapKeyColumn, but I think you would be better off having an entity.
I have a problem very similar to this: How do I join tables on non-primary key columns in secondary tables?
But I'm not sure if I can apply the same solution.
I have two tables like these:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID INTEGER NOT NULL,
DETAIL_ID INTEGER NOT NULL,
PRIMARY KEY( CUSTOMER_ID ),
CONSTRAINT cust_fk FOREIGN KEY( DETAIL_ID ) REFERENCES DETAILS( DETAIL_ID )
)
CREATE TABLE DETAILS
(
DETAIL_ID INTEGER NOT NULL,
OTHER INTEGER NOT NULL,
PRIMARY KEY( DETAIL_ID )
)
I'd like to map these tables to a single class called Customer, so I have:
#Entity
#Table(name = "CUSTOMERS")
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID"))
public class Customer {
#Id
#GeneratedValue
#Column(name = "CUSTOMER_ID")
private Integer id;
#Column(table = "DETAILS", name = "OTHER")
private Integer notes;
// ...
}
but this works only if DETAIL_ID matches CUSTOMER_ID in the primary table.
So my question is: how can i use a foreign-key field in my primary table to join on the primary-key of the secondary table?
UPDATE
I tried to set:
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID", referencedColumnName="DETAIL_ID"))
but when I run the application I get this exception:
org.hibernate.MappingException: Unable to find column with logical name: DETAIL_ID in org.hibernate.mapping.Table(CUSTOMERS) and its related supertables and secondary tables
For anyone looking for an answer to this, using #SecondaryTable is not the way to join two tables with non-primary key columns, because Hibernate will try to assosiate the two tables by their primary keys by default; you have to use #OneToMany review http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/ for a solution, here's a code snippet in case that url stops working:
Customer Class:
#Entity
#Table(name="CUSTOMERS")
public class Customer {
#Id
#GeneratedValue
#Column(name="CUSTOMER_ID")
private Integer id;
#ManyToOne
#JoinColumn(name="DETAIL_ID")
private Details details;
// Getter and Setter methods...
}
Details Class:
#Entity
#Table(name="DETAILS")
public class Details {
#Id
#GeneratedValue
#Column(name="DETAIL_ID")
private int detailId;
#Column(name="OTHER")
private String other;
#OneToMany(mappedBy="details")
private Set<Customer> customers;
// Getter and Setter methods...
}
This is easily accessible through hibernate with the following code:
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("select id, details.other from Customer");
I hope this helps anyone out there spending hours searching for a way to achieve this like I did.
You can use the referenceColumnName attribute of the #PrimaryKeyJoinColumn annotation to define the join column to the referenced table. In fact, by combining use of name/referencedColumnName you can join on arbitrary on both sides, with the constraint that if duplicates are found your ORM provider will throw an exception.