I have two entities viz:
State
#Entity
#Table(name = "State")
public class StateEntity {
#Column(name = "id", length = 36, nullable = false, unique = true)
private String id;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "InsurerId", nullable = false)
private InsurerEntity insurer;
#Column(name ="StateName", length = 50, nullable = false)
private String stateName;
//getters and setters
}
Insurer
#Entity
#Table(name = "Insurer")
public class InsurerEntity {
#Column(name = "InsurerId", length = 36, nullable = false, unique = true)
private String insurerId;
#Column(name = "InsurerName", length = 100, nullable = true)
private String insurerName;
#OneToMany(mappedBy = "state", fetch = FetchType.LAZY)
private List<StateEntity> stateEntityList;
//getters and setters
}
the insurer's id gets saved in state database and I want to retrieve it using hibernate query but I cant't seem to find the solution for that
How to write this query SELECT InsurerId FROM State; in Hibernate query using CriteriaBuilder, CriteriaQuery and Root..
If you want to select all Insurers's Ids for all states:
String selectionQuery = "SELECT s.insurer.insurerId FROM State s";
List<String> insurersIds = session.createQuery(selectionQuery).list();
If you want to select the Insurer's Id of a certain state:
String selectionQuery = "SELECT s.insurer.insurerId FROM State s WHERE s.id = :stateId";
String insurerId = (String) session.createQuery(selectionQuery).setParameter("stateId", stateId).getSingleResult(); //This should be placed in a try/catch block to handle org.hibernate.NonUniqueResultException
Edit:
You should update your Insurer entity as Prasad wrote in his answer.
for this you have to map both the class as in put #oneToMany annotation in class InsurerEntity as well
#OneToMany(fetch = FetchType.LAZY,mappedBy="StateEntity", cascade = CascadeType.ALL)
private List< StateEntity > StateEntitys;
and when you fetch states you will also get object of InsurerEntity in it from where you can access it with the getter
Related
When saving my entities, child entities that work through the #OneToMany relationship are not saved to their tables. I can’t understand what’s the matter.
Employee:
#Entity
#Table(name = "EMPLOYEE", schema = PUBLIC)
public class Employee {
private String name;
private String lastname;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
List<EmployeePhoneNumber> employeePhoneNumbers = new ArrayList<>();
}
EmployeePhoneNumber:
#Entity
#Table(name = "EMPLOYEE_PHONES", schema = PUBLIC)
public class EmployeePhoneNumber {
#Id
#SequenceGenerator(allocationSize = 1, name = "SEQ_EMPLOYEE_PHONES", schema = PUBLIC,
sequenceName = "EMPLOYEE_PHONES_ID_SEQ")
#GeneratedValue(generator = "SEQ_EMPLOYEE_PHONES", strategy = GenerationType.SEQUENCE)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "employee_id", referencedColumnName = "id",
nullable = false, insertable = false, updatable = false)
private Employee employee;
#Column(name = "PHONE_NUMBER", unique = true, nullable = false)
private String phoneNumber;
#Enumerated(EnumType.STRING)
#Column(name = "NUMBER_TYPE", nullable = false)
private PhoneNumberType phoneNumberType;
}
How I set those fields and then save the entity:
EmployeePhoneNumber workPhone = new EmployeePhoneNumber();
workPhone.setPhoneNumber(workPhone);
workPhone.setPhoneNumberType(PhoneNumberType.WORK_PHONE);
EmployeePhoneNumber mobilePhone = new EmployeePhoneNumber();
mobilePhone.setPhoneNumber(mobilePhone);
mobilePhone.setPhoneNumberType(PhoneNumberType.MOBILE_PHONE);
EmployeePhoneNumber corporatePhone = new EmployeePhoneNumber();
corporatePhone.setPhoneNumber(corporatePhoneNumber);
corporatePhone.setPhoneNumberType(PhoneNumberType.CORPORATE_PHONE);
List<EmployeePhoneNumber> employeePhoneNumbers = employee.getEmployeePhoneNumbers();
employeePhoneNumbers.add(workPhone);
employeePhoneNumbers.add(mobilePhone);
employeePhoneNumbers.add(corporatePhone);
employee.setEmployeePhoneNumbers(employeePhoneNumbers);
employeeRepository.save(employee);
Upon completion of the method, I do not have a single error, everything works out correctly, only the tables are not filled - why?
You must also set the Employee reference in EmployeePhoneNumber because Hibernate will use this to save it.
workPhone.setEmployee(employee);
mobilePhone.setEmployee(employee);
corporatePhone.setEmployee(employee);
The best solution would be to create an addEmployeePhoneNumber method on the Employee like this:
public void addEmployeePhoneNumber(EmployeePhoneNumber phoneNumber) {
phoneNumber.setEmployee(this);
employeePhoneNumbers.add(phoneNumber);
}
That way you will not forget to set both sides of the relationship.
I have two tables Company and Employees, one-to-many mapping. Company table contains composite primary key.
I want to search from company table based on primary id but want to put an additional check on the child table.
I want to load only a particular type of employees which I will get in the request. How it can be done in Sprongboot JPA with findById("id");
class Company{
#Id
private String companyId;
#Id
private String stateId;
private String company Name;
#OneToMany(targetEntity = Employees.class, fetch = FetchType.LAZY, cascade = {
CascadeType.ALL }, mappedBy = "company")
private Set<Employees> empList;
}
class Employees{
#Id
private String id;
//foreign key
private String companyId;
//foreign key
private String stateId;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "companyId", referencedColumnName = "companyId", insertable = false, updatable = false, nullable = true),
#JoinColumn(name = "stateId", referencedColumnName = "stateId", insertable = false, updatable = false, nullable = true) })
private Company company;
private int salary;
private String type;
}
Use Filter, which is an alternative of #Where where you can set dynamic value.
Here is the sample
#FilterDef(
name = "employeeTypeFilter",
parameters = #ParamDef(name = "type", type = "string")
)
#Filter(
name = "employeeTypeFilter",
condition = "type > : type"
)
public class Employees {
}
You can enable or disable filter from your code dynamically based on your requirement.
You can use #Where for fixed type
#Where(clause = "type = 'anyEmployeeType'")
private Set<Employees> empList;
For dynamically fetch you can query in Employees repository
List<Employees> findByTypeAndCompany(String type, Company company);
Here's what I'm trying to do...I have a Person
#Entity
#Table(name = "PERSON",
uniqueConstraints = {
#UniqueConstraint(columnNames = {"SSN"})
}
)
#DynamicInsert(true)
#DynamicUpdate(true)
#SelectBeforeUpdate(true)
public class Person implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
#Version
#Column(name = "OBJ_VERSION")
private Timestamp version;
#Id
#Column(name = "SSN", length = 12, nullable = false, insertable = true, updatable = true)
private String ssn;
#Column(name = "LAST_NAME", length = 50, nullable = false, insertable = true, updatable = true)
private String lastName;
#Column(name = "FIRST_NAME", length = 30, nullable = false, insertable = true, updatable = true)
private String firstName;
#Column(name = "MIDDLE_NAME", length = 30, nullable = true, insertable = true, updatable = true)
private String middleName;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "person", cascade = CascadeType.ALL)
private Passport passport;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Citizenship> citizenship = new HashSet<>();
// Getters and setters left out for brevity
and each person can have one Passport
#Entity
#Table(name = "PASSPORT",
uniqueConstraints = {
#UniqueConstraint(columnNames = {"SSN", "PASSPORT_NUMBER"})
}
)
#DynamicInsert(true)
#DynamicUpdate(true)
#SelectBeforeUpdate(true)
public class Passport implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
#Version
#Column(name = "OBJ_VERSION")
private Timestamp version;
#Id
#Column(name = "SSN", length = 12, nullable = false, insertable = true, updatable = true)
private String ssn;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SSN")
#MapsId
private Person person;
#Column(name = "EXPIRATION_DATE", nullable = true, insertable = true, updatable = false)
private GregorianCalendar expirationDate;
#Column(name = "ISSUING_COUNTRY", nullable = true, insertable = true, updatable = false)
private String issuingCountry;
#Column(name = "PASSPORT_NUMBER", nullable = false, insertable = true, updatable = false)
private String passportNumber;
// Getters and setters left out for brevity
This works, each person can have one Passport and the Passport.ssn is assigned the value of the Person.ssn. This is being done because SSN is a unique identifier and it avoids the need for link tables.
Each person can also have a Citizenship
#Entity
#Table(name = "CITIZENSHIP")
#DynamicInsert(true)
#DynamicUpdate(true)
#SelectBeforeUpdate(true)
public class Citizenship implements java.io.Serializable {
private static final long serialVersionUID = 6732775093033061190L;
#Version
#Column(name = "OBJ_VERSION")
private Timestamp version;
#EmbeddedId
private CitizenshipId citizenshipId;
#Column(name = "DATE_OF_CITIZENSHIP")
private GregorianCalendar dateOfCitizenship;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SSN")
#MapsId("ssn")
private Person person;
// Getters and setters left out for brevity
I have successfully added a person with a passport and a person without a passport. I have added a third person with a passport and dual citizenship with
// This person has a passport and is a dual citizen.
person = new Person();
person.setSsn("654-89-7531");
person.setFirstName("Lois");
person.setLastName("Lane");
passport = new Passport();
passport.setExpirationDate(new GregorianCalendar());
passport.setIssuingCountry("USA");
passport.setPassportNumber("987654");
Set<Citizenship> citizenshipSet = new HashSet<>();
CitizenshipId citizenshipId = new CitizenshipId();
citizenshipId.setCountry("USA");
Citizenship c = new Citizenship();
c.setDateOfCitizenship(new GregorianCalendar());
c.setCitizenshipId(citizenshipId);
c.setPerson(person);
citizenshipSet.add(c);
citizenshipId = new CitizenshipId();
citizenshipId.setCountry("CAN");
c = new Citizenship();
c.setDateOfCitizenship(new GregorianCalendar());
c.setCitizenshipId(citizenshipId);
c.setPerson(person);
citizenshipSet.add(c);
person.setPassport(passport);
passport.setPerson(person);
session.saveOrUpdate(person);
for(Citizenship citizen : citizenshipSet) {
session.saveOrUpdate(citizen);
}
session.flush();
session.clear();
This looks weird/inefficient to me, but it does work (tips for improvement would be appreciated). But as desired, the Person.ssn is carried into the Citizenship. Here's the problem:
The Person with dual Citizenship currently has citizenship in USA and Canada. Let's assume this is wrong and the Person has citizenship in USA and Mexico, which means the CitizenshipId.country needs to change from "CAN" to "MEX". I have tried a bunch of variations of code like
Criteria citCriteria = session.createCriteria(Citizenship.class);
citCriteria.add(Restrictions.eq("citizenshipId.ssn", "654-89-7531"));
List<Citizenship> citizenship = citCriteria.list();
for(Citizenship c : citizenship) {
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
session.saveOrUpdate(c);
session.flush();
session.clear();
}
}
With "show_sql" on, this doesn't perform an update, even though I can see the values change when debugging. I did try an evict(), then set the country, then saveOrUpdate, which made a new entry (I figured it would).
Phew...the question is: How can the values in an Embeddable class be updated when that class is being used as an EmbeddedId? I feel like I'm close but just missing one thing...
Thanks.
Adding CitizenshipID for reference
#Embeddable
public class CitizenshipId implements Serializable {
private static final long serialVersionUID = 6732775093033061190L;
String ssn;
String country;
// Omitted getters, setters, constructors, hashcode, and equals
Have you tried:
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
c.getPerson().getCitizenship().add(c); // TRY ADDING THIS
session.saveOrUpdate(c);
session.flush();
session.clear();
}
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
// TRY ADDING THIS ------------------------
//session.evict(c);
CitizenshipId cid = new CitizenshipId();
cid.setSsn(c.getCitizenshipId().getSsn();
cid.setCountry("MEX");
c.setCitizenshipId(cid); // references new CID -- should issue update
// -----------------------------------------
session.saveOrUpdate(c);
session.flush();
session.clear();
}
I removed the .evict due to the description in the API:
Remove this instance from the session cache. Changes to the instance
will not be synchronized with the database. This operation cascades to
associated instances if the association is mapped with
cascade="evict".
Topic How to update values associated with Primary Key in Spring-JPA is inline with what Dan posted above about creating a new object with the old object Id. However, topic Hibernate - update the primary key 'id' column in the table does state that Hibernate doesn't allow updates to primary keys.
The objective here was to create a Person with a(n) SSN, possibly with a Passport, and a Citizenship. SSN is intended to be the primary key so I mapped Person to Passport and Citizenship and used SSN as the JoinColumn.
Person to Passport is a one to one relationship, so that wasn't a problem.
Person to Citizenship is a one to many relationship. This relationship means I had to create an embeddable ID. To make each Citizenship unique the embeddable class CitizenshipId was created with SSN and Country.
Using the accepted answer for Hibernate - update the primary key 'id' column in the table I changed the variations of
Criteria citCriteria = session.createCriteria(Citizenship.class);
citCriteria.add(Restrictions.eq("citizenshipId.ssn", "654-89-7531"));
List<Citizenship> citizenship = citCriteria.list();
for(Citizenship c : citizenship) {
if("CAN".equalsIgnoreCase(c.getCitizenshipId().getCountry())) {
session.evict(c);
c.getCitizenshipId().setCountry("MEX");
session.saveOrUpdate(c);
session.flush();
session.clear();
}
}
to
Query query=session.createQuery("update Citizenship set country = :country1 where ssn = :ssn and country = :country2")
.setString("country1", "MEX").setString("ssn", "654-89-7531").setString("country2", "CAN");
query.executeUpdate();
And an update did occur. Being unable to make an update via typical code (use criteria to get data, update it, then call saveOrUpdate) but being able to make an update via a query doesn't make a whole lot of sense to me. I know that key management is more times than not best left to the database, but when a unique value such as SSN is being used there is no need for another key. If an ID is identified within the code without a generation strategy it stands to reason that the IDs can be updated...JMHO.
Thanks to Dan for his ideas. I hope this topic and its references helps others.
Given the following two entities:
public class Performer {
#Id
private Long id;
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "performers_characters",
joinColumns = #JoinColumn(name = "performer_id", nullable = false, updatable = false),
inverseJoinColumns = #JoinColumn(name = "character_id", nullable = false, updatable = false))
private Set<Character> characters = Sets.newHashSet();
}
And:
public class Character {
#Id
#Column(nullable = false)
private Long id;
#Column(nullable = false)
private String name;
private Boolean alive;
private String placeOfBirth;
#ManyToMany(mappedBy = "characters")
private Set<Performer> performers = Sets.newHashSet();
}
I want to create query, using Criteria Builder, that will generate the equivalent SQL:
SELECT performer0_.id AS id1_11_0_,
character2_.id AS id1_0_1_,
performer0_.name AS name7_11_0_,
character2_.name AS name12_0_1_,
characters1_.character_id AS character_id2_12_0__
FROM performer performer0_
INNER JOIN performers_characters characters1_ ON performer0_.id=characters1_.performer_id
INNER JOIN character character2_ ON characters1_.character_id=character2_.id
So basically, everything from Performer should be selected, but from Character, only id and name should be selected.
Currently I'm stuck with the following:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Performer> baseCriteriaQuery = criteriaBuilder.createQuery(Performer.class);
Root<Performer> baseRoot = baseCriteriaQuery.from(Performer.class);
baseCriteriaQuery.select(baseRoot);
baseRoot.fetch("characters");
TypedQuery<Performer> baseTypedQuery = entityManager.createQuery(baseCriteriaQuery);
List<Performer> baseEntityList = baseTypedQuery.getResultList();
This work fine, except that from both Character and Performer, all columns are selected.
With JPA this would only be possible if you specify the fields that you don't want to load as lazy.
#Basic(fetch=FetchType.LAZY)
private String placeOfBirth;
#Basic(fetch=FetchType.LAZY)
private Boolean alive;
But the question is why do you want to do that? Both fields are small items and with LAZY you have more SQL calls to fetch the data.
Brand
public class Brand implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "BrandID", nullable = false)
private Integer brandID;
#Basic(optional = false)
#Column(name = "BrandName", nullable = false, length = 100)
private String brandName;
#Basic(optional = false)
#Column(name = "Description", nullable = false, length = 1000)
private String description;
#Column(name = "Is_Visible")
private Boolean isVisible;
#JoinTable(name = "brandcategory", joinColumns = {
#JoinColumn(name = "BrandID", referencedColumnName = "BrandID")}, inverseJoinColumns = {
#JoinColumn(name = "CategoryID", referencedColumnName = "CategoryID")})
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Category> categoryCollection;
#OneToMany(mappedBy = "brand", fetch = FetchType.EAGER)
private Collection<Product> productCollection;
I want to retrive the Brand IDs from table brandcategory whoes categoryID = :categoryID
how can i createnamed query for it in entity brand?
this does not work:
#NamedQuery(name = "Brand.getBrandListByCategory",
query = "SELECT b FROM Brand b WHERE b.brandID =
(SELECT bc.brandID
FROM b.brandctegory bc
WHERE bc.category.categoryID = :categoryID)")
If I understand correctly, you want all the brands belonging to a category. Why don't you simply make the association bidirectional. You could then just do:
Category category = em.find(Category.class, categoryId);
return category.getBrands();
If it's unidirectional, then you'll need a query, but it's much simpler that the one you tried:
select b from Brand b inner join b.categoryCollection category
where category.id = :categoryId;
Your query doesn't make sense: it uses a non-existing association (b.brandcategory). Remember that JPQL uses entities, their persistent fields and associations to other entities. And nothing else. Tables don't exist in JPQL.
AFAIK, you cant go out of a entity boundary, when creating queries in entity class.
Instead use .createNativeQuery() method of the entity manager, to create complex and mixed queries.