I am developing a Java Desktop Application and using JPA for persistence. I have a problem mentioned below:
I have two entities:
Country
City
Country has the following attribute:
CountryName (PK)
City has the following attribute:
CityName
Now as there can be two cities with same name in two different countries, the primaryKey for City table in the datbase is a composite primary key composed of CityName and CountryName.
Now my question is How to implement the primary key of the City as an Entity in Java
#Entity
public class Country implements Serializable {
private String countryName;
#Id
public String getCountryName() {
return this.countryName;
}
}
#Entity
public class City implements Serializable {
private CityPK cityPK;
private Country country;
#EmbeddedId
public CityPK getCityPK() {
return this.cityPK;
}
}
#Embeddable
public class CityPK implements Serializable {
public String cityName;
public String countryName;
}
Now as we know that the relationship from Country to City is OneToMany and to show this relationship in the above code, I have added a country variable in City class.
But then we have duplicate data(countryName) stored in two places in the City class' object: one in the country object and other in the cityPK object.
But on the other hand, both are necessary:
countryName in cityPK object is necessary because we implement composite primary keys in this way.
countryName in country object is necessary because it is the standard way of showing relashionship between objects.
How to get around this problem?
countryName in CityPK should be marked read-only using #Column(insertable = false, updatable = false) and both countryNames should be mapped to the same column (using name property):
#Entity
public class City implements Serializable {
#EmbeddedId
private CityPK cityPK;
#ManyToOne
#JoinColumn(name = "countryName")
private Country country;
}
#Embeddable
public class CityPK implements Serializable {
public String cityName;
#Column(name = "countryName", insertable = false, updatable = false)
public String countryName;
}
IMO the proper way to deal with such issues would be to use a generated internal (typically Long) ID instead of a natural primary key - this eliminates the whole problem. Of course, this requires a modification of your DB schema, but from your post I assume that this is possible.
#Entity
public class City implements Serializable {
private Long id;
private String name;
private Country country;
#Id
#GeneratedValue
#Column(name = "CITY_ID")
public Long getId() {
return this.id;
}
private void setId(Long id) {
this.id = id;
}
// more getters, setters and annotations
}
Related
I'm working with Spring Data JPA and I'm trying to create 4 different entities that will have exactly the same fields but they will be stored in 4 different tables.
This is my key class
public class IndexId implements Serializable {
private int seqNo;
private String index;
// getters and setters
}
Then I have the base class:
#MappedSuperclass
public class BaseIndex {
#Id
#Column(name = "seq_no", nullable = false)
protected int seqNo;
#Id
#Column(name = "index", nullable = false)
protected String index;
#Column(name = "value", nullable = false)
protected String value;
//getters/setters
}
Then my entity that will store in the database:
#Entity
#IdClass(IndexId.class)
#Table(name = "bibliographic_single_index")
public class BibliographicSingleIndex extends BaseIndex implements Serializable { }
This is the error I get: Persistent entity 'BibliographicSingleIndex' should have primary key .
I also tried with the properties declared as private and the articles I see on this subject seem to do the same thing.
With these pieces of code is it possible to identify what I'm doing wrong?
I believe every entity needs a separate java class for the id class. You wouldn't have the problem with embedded ids I think.
I am using Spring-Boot with JPA and a MySQL backend. Now I got quite confused about the repositories Spring-Boot provides. I know these are quite powerful (and seem to be quite useful since they can shorten your code a lot). Still, I do not understand how to represent Joins within them, since the result-set should be a combination of specified attributes in the select of a few Entities.
Now let's assume we have three tables Book, Author, AuthorOfBook, where the last one is simply connecting Book and Author by a combined Primary key. I guess we had the following Java-Classes:
Entity Book:
#Entity
#Table(name="BOOK")
public class Book {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "TITLE")
private String title;
}
Entity Author
#Entity
#Table(name="AUTHOR")
public class Author {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "LASTNAME")
private String lastname;
#Column(name = "FIRSTNAME")
private String firstname;
//Let's assume some getters and setters and a constructor
}
Entity AuthorOfBook:
#Entity
#Table(name="BOOK")
public class Book {
#EmbeddedId
private AuthorOfBookId pk;
}
An Embedded ID
#Embeddable
public class AuthorOfBookId implements Serializable {
private int authorId;
private int bookId;
}
Repository
#Repository
public interface AuthorOfBookRepository extends JpaRepository<,AuthorOfBookId> {
}
Now how would I represent that query:
SELECT b.name, a.firstname, a.lastname from AuthorOfBook ab inner join Book b on b.id = ab.book_id inner join Author a on a.id = ab.author_id where a.lastname = :lastname;
in my repository? I know the signature would need to be like
#Query([the query string from above])
public (...) findAuthorAndBookByAuthorLastname(#Param("lastname") String lastname);
but I cannot make out what Type the return would be like. What is that method returning? (simply AuthorOfBook would not work I guess)
You don't want AuthorOfBook as a separate Entity. Book should have a field of type Author as a #ManyToOne relationship. That way, given any Book, you can find the author's details.
If you want to handle audits fields you can do something like this:
Audit class
#Embeddable
public class Audit {
#Column(name = "created_on")
private Timestamp createdOn;
#Column(name = "updated_on")
private Timestamp updatedOn;
#Column(name = "is_deleted")
private Boolean isDeleted;
//getters and setters
}
AuditListener to update automatically audits fields
public class AuditListener {
private Long loggedUser = 1001L;
/**
* Method to set the fields createdOn, and isDeleted when an entity is persisted
* #param auditable
*/
#PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if (audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setIsDeleted(Boolean.FALSE);
audit.setCreatedOn(Timestamp.from(Instant.now()));
}
/**
* Method to set the fields updatedOn and updatedBy when an entity is updated
* #param auditable
*/
#PreUpdate
public void setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(Timestamp.from(Instant.now()));
}
}
And add this to the entities
#EntityListeners(AuditListener.class)
public class Book implements Auditable {
#Embedded
private Audit audit;
I am designing two entities, one called Country and one called CountryDetail. From the perspective of tables, the COUNTRY table will be the parent table, and the COUNTRY_DETAIL table will be the child table. In the COUNTRY table, there will be a unique attribute called COUNTRY_CODE (note this is not a primary key; the primary key will be a numeric sequence based value). This code will be a foreign key to connect to the child table, and in this child table, each COUNTRY_CODE from the parent table will have 3 entries to represent the name of the country in 3 different languages. Following are the entity classes:
Country.java
#Entity
public class Country
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "COUNTRY_ID")
private long id;
#Column(name="COUNTRY_CODE", nullable = false, unique = true)
private String countryCode;
/*public getters*/
}
CountryDetail.java
#Entity
public class CountryDetail
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "COUNTRY_DETAIL_ID")
private long id;
#ManyToOne
#JoinColumn(name="COUNTRY_CODE", referencedColumnName = "COUNTRY_CODE")
private Country country;
#Column(nullable = false)
private String languageCode;
#Column(nullable = false, unique = true)
private String countryNameInLanguage;
/*public getters*/
}
My question is, how can I write a custom "findBy..." interface method inside an extension of JpaRepository that is typed to a Country that would return me a collection of CountryDetail elements that match an input parameter for the languageCode attribute of the CountryDetail class?
public interface CountryRepository extends JpaRepository<Country, Long>
I know how to do it if the repository was typed to CountryDetail instead of Country, but I would like to know how to do it going via the parent entity rather than via the child entity directly, even though the input parameter (languageCode) exists only in the child entity.
Thank you.
It is possible but complicated : Please see an example below :
Parent :
#Entity
#Table(name="PARENT")
public class Parent {
#Id
#Column(name="PARENT_ID")
private int parentId;
#Column(name="PARENT_NAME")
private String parentName;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Child child;
}
Child :
#Entity
#Table(name="CHILD")
public class Child {
#Id
#Column(name="CHILD_ID")
private int childId;
#Column(name="CHILD_NAME")
private String childName;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Parent parent;
}
Define an interface to load child entity from parent repository :
ChildEntity:
public interface ChildEntity {
#Value("#{target.child.childId}")
int getChildId() ;
#Value("#{target.child.childName}")
String getChildName();
}
Parent Repository :
public interface ParentRepository extends JpaRepository<Parent, Integer> {
public ChildEntity findByParentName(String parentName);
}
Test class:
ChildEntity chi=rep.findByParentName("<<NAME>>");
System.out.println(chi.getChildId()+" "+chi.getChildName());
Output:
CHILD ID 1000 CHILD NAME child1
I want to use one class to map three tables. I know javax.persistance provides the #SecondaryTable annotation to map two tables to one class.
Below is the code, where I have used #SecondaryTable. It allows me to define only one secondary table. But I need 3 tables to be used by the same class.
#Entity
#Table(name = "table1")
#SecondaryTable(name="table2")
public class TableConfig
implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "mac", table= "table1")
private String uniqueIdentifier;
I want to use one class to map three tables, From what I know is that javax.persistance provides #SecondaryTable annotation to map two tables to one class
use #SecondaryTables to map more than one table.
You can map a single entity bean to several tables using the #SecondaryTables class level annotations. To express that a column is in a particular table, use the table parameter of #Column or #JoinColumn.
for example there is 3 entity's namely: Name , Address & Student:
Name entity will look like:
#Entity
#Table(name="name")
public class Name implements Serializable {
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name;
public Name(){}
public Name(int id,String name){
this.id=id;
this.name=name;
}
//getters and setters
}
Address entity will look like:
#Entity
#Table(name="address")
public class Address implements Serializable {
#Id
#Column(name="id")
private int id;
#Column(name="address")
private String address;
public Address(){}
public Address(int id, String address) {
super();
this.id = id;
this.address = address;
}
//getters and setters
}
Student entity will look like:
#Entity
#Table(name="student")
#SecondaryTables({
#SecondaryTable(name="name", pkJoinColumns={
#PrimaryKeyJoinColumn(name="id", referencedColumnName="student_id") }),
#SecondaryTable(name="address", pkJoinColumns={
#PrimaryKeyJoinColumn(name="id", referencedColumnName="student_id") })
})
public class Student implements Serializable {
#Id
#Column(name="student_id")
private int studentId;
#Column(table="name")
private String name;
#Column(table="address")
private String address;
public Student(){}
public Student(int studentId){
this.studentId=studentId;
}
//getters and setters
}
Store like:
Student s= new Student(1);
session.save(s);
Name n=new Name(s.getStudentId(),"Bilal Hasan");
session.save(n);
Address address = new Address(s.getStudentId(), "India");
session.save(address);
Student ob = (Student)session.get(Student.class, s.getStudentId());
System.out.println(ob.getStudentId());
System.out.println(ob.getName());
System.out.println(ob.getAddress());
ouput:
1
Bilal Hasan
India
you can define one class like below :
#Entity
#Table(name="table1")
#SecondaryTables({
#SecondaryTable(name="table2", pkColumnJoins={#PrimaryKeyJoinColumn(name = "id")}),
#SecondaryTable(name="table3", pkColumnJoins={#PrimaryKeyJoinColumn(name = "id")})
})
public class TestEntity {
#Id
#GeneratedValue
private int id;
private String field1;
#Column(name="column2", table="table2")
private String field2;
#Column(name="column3", table="table3")
private String field3;
getter and setter...
}
In your DB, should has three table, and all of them should has the same primary key "id".
then, use can test like this:
TestEntity test = new TestEntity();
test.setField1("field1");
test.setField2("field2");
test.setField3("field3");
em.merge(test);
after test, in your DB, you will find one record in each table:
table1:
1, field1
table2:
1, field2
table3:
1, field3
all of them will share the primary key value. Hope this will help you.
In Hibernate mapping file you can specify the entity-name mapping with virtual name along with polymorphism="explicit" and class name would be physical class name. Like that you may do multiple mappings. While loading the object use entityname (virtual name).
Suppose i want to have a composite key as street, city for purchase order entity.
Below is how i identify doing it,
#Embeddable
public class BillingAddress implements Serializable {
private String street;
private String city;
public BillingAddress(){
}
public BillingAddress(String street, String city) {
this.street = street;
this.city = city;
}
//with getters and setters
}
#Entity
#IdClass(BillingAddress.class)
public class PurchaseOrder {
public PurchaseOrder(BillingAddress billingAddress) {
street = billingAddress.getStreet();
city = billingAddress.getCity();
}
#Id
#AttributeOverrides({
#AttributeOverride(name = "street", column = #Column(name = "STREET")),
#AttributeOverride(name = "city", column = #Column(name = "CITY")) })
private String street;
private String city;
private String itemName;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
}
I want to understand what is really #AttributeOverrides annotation do? Even i change to colomn name to something STREET1 i still see the table created with column name STREET. So what is column = #Column(name = "STREET")) doing here.
Also instead of constructore taking the BillingAddress i can have it like a field of PurchaseOrder class right like,
public class PurchaseOrder {
BillingAddress billingAddress;
}
In this case how this going to change?
Do i still need to have private String street; private String city; in PurchaseOrder?
Finally i read that using composite keys should be avoided in new data base system design which using composite primary key is applicable a situation where in order to map the legacy data base tables with out changing the data base table structure right? Is that statement a valid one?
//Edit question
Saving purchase order which billing address is in the field,
PurchaseOrder purchaseOrder = new PurchaseOrder();
purchaseOrder.setItemName("name");
BillingAddress billingAddress = new BillingAddress();
billingAddress.setCity("c1"); billingAddress.setStreet("s1"); purchaseOrder.setBillingAddress(billingAddress);
session.save(purchaseOrder);
There's are few question you asked, I tried to go through all of them and answer each one:
What does #AnnotationOverride do?
answer here: What does #AttributeOverride mean?
The second question is a bit unclear to me but I presume you're asking whether you have to include all the fields from the composite key in the PurchaseOrder class.
No, I don't think so. Here's an example I've put together real fast:
#Entity
#Table(name = "PURCHASE_ORDER")
public class PurchaseOrder{
#Id
private BillingAddress billingAddress;
//getters & setters
#Embeddable
public static class BillingAddress implements Serializable {
#Column(name = "street")
private String street;
#Column(name = "city")
private String city;
#Column(name = "itemName")
private String itemName;
//getters & setters
}
}
Don't worry about the syntax, just the structure. You can even add extra field into PurchaseOrder which isn't an id.
Should I use composite keys or not?
answer here: Should I use composite primary keys or not?
Well, your PurchaseOrder class does not extend from a mapped entity of any kind, and neither do the properties that you are (currently) applying the #AttributeOverrides to. So, there is nothing to actually override and your JPA provider is simply ignoring the annotations. What I think you are trying to do is define an embedded id for an entity, while overriding some of the column mappings for that id. You can do this with some modifications to your current code:
#Entity
public class PurchaseOrder {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "street", column = #Column(name = "BILLING_STREET")),
#AttributeOverride(name = "city", column = #Column(name = "BILLING_CITY")) })
private BillingAddress billingAddress;
private String itemName;
// Constructors, Getters/Setters
}
Note that I've changed the names of the overridden attributes, since with your current example, the embedded id name and overridden names are the same.