I am new to Hibernate / Spring, trying to map legacy database while creating a little web utility, which should ease some work for the colleagues. As I had mapped the entities and have access to the underlying data, I have some issues further down the road. To make long story short, I have two entites, Customer
#Entity
public class Customer implements Serializable{
#Id
#Column(name = "RecordID")
private Integer id;
#Column(name = "CUSTOMERNAME1")
private String name;
#OneToMany
#JoinColumn(name="CUSTOMER1", referencedColumnName="CUSTOMERNAME1")
private List<Contract> contracts;
}
and Contract:
#Entity
public class Contract implements Serializable {
#Id
#Column(name = "RECORDID") //RecordID
private Integer id;
#Column(name = "CONTRACTID1") //ContractID1
private String contractId;
#Column(name = "CUSTOMER1") //Customer1
private String customerName;
//#ManyToOne
//private Customer customer; // how can I write the reverse mapping?
}
The mapping from Customer to Contracts works (one customer can have many contracts, I can get them all using List contracts field in customer, but my question is, how can I achieve the reverse - that is, get the customer, to which to contract is mapped?
The Java API has some examples of how to use the ManyToOne annotation:
http://java.sun.com/javaee/6/docs/api/javax/persistence/ManyToOne.html
something like the below:
Customer class:
#OneToMany(mappedBy="customer", cascade=CascadeType.ALL)
private List<Contract> contracts;
Contract class:
#ManyToOne
#JoinColumn(name="customer_id", nullable=false)
private Customer customer;
Related
#Entity
public class TestPair implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#OneToOne(cascade = {CascadeType.ALL})
#JsonProperty("target_Test")
private Test targetTest;
#OneToOne(cascade = {CascadeType.ALL})
#JsonProperty("source_Test")
private Test sourceTest;
...}
#Entity
public class Test {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private String shortname;
...}
I have a List<TestPair> that i want to persist using spring boot CrudRepository.
TestPair's contain Test's and these Test's are the same along some of the TestPair's.
When i use the saveAll method, it persists the objects but the Test's that are equal it inserts new one's, creating lots of repeating elements in the db.
I've create the equals and hashcode for this classes.
Check the auto generation logic for both the ids. If it is auto incremental then all the created entity will be different hence new entry is getting created every time.
I've found a solution.
Instead of using the crud saveAll for the Hashset, i'm saving one by one.
for(TestPair tPair : TPairs){
testRepository.save(tPair.getSourceTest());
testRepository.save(tPair.getTargetTest());
testPairRepository.save(tPair);
}
And changed the classes to:
#Entity
public class TestPair implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#OneToOne(cascade = {CascadeType.MERGE})
#JsonProperty("target_Test")
private Test targetTest;
#OneToOne(cascade = {CascadeType.MERGE})
#JsonProperty("source_Test")
private Test sourceTest;
}
#Entity
public class Test {
private String name;
#Id
private String shortname;
}
This way hibernate merges the objects, not creating new one's. Thank you all!
I can't understand how works oneToMany and manyToOne in JPA. For a sample I have to entity.
#Entity
public class Customer {
#Id
#GeneratedValue
private long id;
private String name;
private List<Skills> skillList
}
and another one
#Entity
public class SkillList {
private String skillName;
private byte skillLevel;
}
How to correct link this entities? Also If anyone can explain it in an accessible way.
In database one to many relationship is achieved by foreign key.
In order to link two entities in Java according to JPA specification you should use #ManyToOne annotation or both #ManyToOne and #OneToMany if you need bidirectional association.
#Entity
public class Customer {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "customer")
private List<Skill> skills;
}
#Entity
public class Skill {
#Id
#GeneratedValue
private Long id;
private String skillName;
private byte skillLevel;
#ManyToOne
private Customer customer;
}
It will generate two tables in the database. Table SKILL has column CUSTOMER_ID which relates to CUSTOMER table.
I need to have a friendship relationship. I have a friendship class with two primary keys that each is a Member class. I am receiving following exception:
org.hibernate.MappingException: Foreign key (FK_8ynretl1yt1xe3gcvfytrvpq:Friendship [])) must have same number of columns as the referenced primary key (Member [username])
Friendship
#Entity
public class Friendship implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1234656876554786549L;
#Id
#ManyToOne
Member requester;
#Id
#ManyToOne
Member friend;
#Temporal(javax.persistence.TemporalType.DATE)
Date date;
Member
#Entity
public class Member {
#Id
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "username")
Credential credential;
#Column(nullable = false)
String fname;
#Column(nullable = false)
String lname;
#Column(nullable = false)
short gender;
Credential
#Entity
public class Credential {
#Id
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String authority;
#Column(nullable = false)
private boolean enabled;
Putting aside that Member and Credential should implement Serializable if multiple id properties without identifier type are used, your mappings are good, and this seems to be a bug in Hibernate.
Solution 1
I managed to make this work by declaring referencedColumnName in friend and requester associations in Friendship:
#Id
#ManyToOne
#JoinColumn(referencedColumnName = "username")
Member requester;
#Id
#ManyToOne
#JoinColumn(referencedColumnName = "username")
Member friend;
This way we explicitly tell Hibernate which columns the composite id references, so that it does not have to figure it out itself.
Solution 2
The solution 1 made me think of what could be the cause of the bug in Hibernate. It seems that it is somehow affected by the order in which Hibernate processes the entity mappings. If you explicitly declare the referenced column, everything works fine, otherwise it seems that Hibernate does not know all the details about the referenced column at the time it builds the composite key.
So I changed the order in which I add annotated classes to the session factory configuration to:
Credential
Member
Friendship
and then everything worked with your original mappings (after implementing Serializable in Member and Credential).
I added the classes in this order programmatically to the Configuration class, but I assume the same effect could be achieved by specifying this order in the persistence.xml or hibernate.cfg.xml:
<class>Credential</class>
<class>Member</class>
<class>Friendship</class>
Nevertheless, this solution is just for demonstrative purposes (you or someone else can later reorder the classes without keeping this issue in mind), so I suggest using solution 1.
Note
You know your use cases better, but in my personal opinion you should use #IdClass or #EmbeddedId since they are standardized in JPA; multiple id properties without identifier type is a Hibernate specific feature. Besides being able to easier construct the primary key object by which you will search and query the corresponding entities, a dedicated PK object is usually much lighter and offers better performance when serialized, especially if second level cache is enabled.
You have to add a separate ID field to the Member class for the #MapsID annotation to map. Like this:
#Entity
public class Member implements Serializable {
#Id
private String username;
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "username")
Credential credential;
#Column(nullable = false)
String fname;
#Column(nullable = false)
String lname;
#Column(nullable = false)
short gender;
}
You are missing the modeling of the primary key in the Friendship class.
For example:
#Embeddable
public class FriendshipPK implements Serializable
{
#Column(name = "requester_id")
protected String requesterId;
#Column(name = "friend_id")
protected String friendId;
}
Friendship class can now be modified as follows:
#Entity
public class Friendship implements Serializable
{
#EmbeddedId
protected FriendshipPK friendshipId = new FriendshipPK();
#ManyToOne
#MapsId("requesterId")
Member requester;
#ManyToOne
#MapsId("friendId")
Member friend;
#Temporal(javax.persistence.TemporalType.DATE)
Date date;
}
I have updated the Member class slightly:
#Entity
public class Member implements Serializable
{
#Id
protected String memberId;
#MapsId
#OneToOne(optional = false)
#JoinColumn(name = "username")
Credential credential;
#Column(nullable = false)
String fname;
#Column(nullable = false)
String lname;
#Column(nullable = false)
short gender;
}
I removed the cascade from Member class, and created the credentials objects first. But you can change this as fit.
In the Friendship class try specifying the #JoinColumn as well:
#Entity
public class Friendship implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "username")
Member requester;
...
}
I am a new bee in Hibernate and I am using PostgreSQL 9.3, JDK 1.7, Hibernate 4.0.2
I am trying to save a Customer who Has-a relationship with Address i.e., One-To-Many Relation.
While saving the Customer i am getting the Exception:
javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.cust.entities.Address
Customer Entity:
#Entity
#Table(name="customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "CustomerIdSeq", sequenceName = "c_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CustomerIdSeq")
#Column (name="c_id")
private Long cId;
#Column(name="cname")
private String cname;
//bi-directional many-to-one association to Address
#OneToMany(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name="c_id")
private List<Address> address;
//getters and setters
}
Address Entity:
#Entity
#Table(name="address")
public class Address {
#Id
#Column(name="c_id")
private Long cId;
#ManyToOne
#PrimaryKeyJoinColumn(name="c_id", referencedColumnName="c_id")
private Customer customer;
#Column(name="street")
private String street;
#Column (name="city")
private String city;
//getters and setters
public void setCustomer(Customer customer) {
this.customer= customer;
this.cId= customer.getCId();
}
}
I had tried some thing which is similar to Java Persistence/Identity & Sequencing
I suspect that the Address record doesn't have an ID when calling save().
You're missing the #GeneratedValue tag in that class, and if not specified, it defaults to "assigned" value.
If you're not assigning a value to Address.cId before calling save(), you'll see this problem. Post all relevant code if this isn't the cause of the issue.
EDIT: Looking at your table structure, Address should really have it's own ID in the schema design, and have a foreign key(FK) reference to Customer.ID.
Form the comments to the previous answer and from your existing table structure, you may want to consider mapping Address as an Embeddable rather than as an Entity:
This is similar to a OneToMany, except the target object is an
Embeddable instead of an Entity. This allows collections of simple
objects to be easily defined, without requiring the simple objects to
define an Id or ManyToOne inverse mapping. ElementCollection can also
override the mappings, or table for their collection, so you can have
multiple entities reference the same Embeddable class, but have each
store their dependent objects in a separate table.
http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection
The mappings would then be as follows. Address will have no persistent identity of its own and can only exist as part of an Entity - currently customer but no reason you cannot use it with other entities requiring an address.
Customer:
#Entity
#Table(name="customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "CustomerIdSeq", sequenceName = "c_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CustomerIdSeq")
#Column (name="c_id")
private Long cId;
#Column(name="cname")
private String cname;
#ElementCollection
#CollectionTable(name = "customer_address", joinColumns = #JoinColumn(name = "c_id")
private List<Address> addresses;
}
Address:
#Embeddable
public class Address {
#Column(name="street")
private String street;
#Column (name="city")
private String city;
}
As per Hibernate documentation, there are multiple annotations available if we want to use Map as an association between our entities. The doc says:
Alternatively the map key is mapped to a dedicated column or columns.
In order to customize the mapping use one of the following
annotations:
#MapKeyColumn if the map key is a basic type. If you don't specify the
column name, the name of the property followed by underscore followed
by KEY is used (for example orders_KEY). #MapKeyEnumerated /
#MapKeyTemporal if the map key type is respectively an enum or a Date.
#MapKeyJoinColumn/#MapKeyJoinColumns if the map key type is another
entity. #AttributeOverride/#AttributeOverrides when the map key is a
embeddable object. Use key. as a prefix for your embeddable object
property names. You can also use #MapKeyClass to define the type of
the key if you don't use generics.
By doing some examples I am able to understand that #MapKey is just used to map the key to a property of target entity and this key is used only for fetching records. #MapKeyColumn is used to map the key to a property of target entity and this key is used to save as well as fetching records. Please let me know if this is correct?
Also please let me know when I need to use #MapKeyJoinColumn/#MapKeyJoinColumns & #MapKeyEnumerated / #MapKeyTemporal
Thanks!
When you use a Map you always need to associate at least two entities. Let's say we have an Owner entity that relates to the Car entity (Car has a FK to Owner).
So, the Owner will have a Map of Car(s):
Map<X, Car>
#MapKey
The #MapKey will give you the Car's property used to group a Car to its Owner. For instance, if we have a vin (Vehicle Identification Number) property in Car, we could use it as the carMap key:
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKey(name = "vin")
private Map<String, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
private String vin;
}
#MapKeyEnumerated
The #MapKeyEnumerated will use an Enum from Car, like WheelDrive:
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKeyEnumerated(EnumType.STRING)
private Map<WheelDrive, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
#Column(name = "wheelDrive")
#Enumerated(EnumType.STRING)
private WheelDrive wheelDrive;
}
public enum WheelDrive {
2WD,
4WD;
}
This will group cars by their WheelDrive type.
#MapKeyTemporal
The #MapKeyTemporal will use a Date/Calendar field for grouping, like createdOn.
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKeyTemporal(TemporalType.TIMESTAMP)
private Map<Date, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="created_on")
private Calendar createdOn;
}
#MapKeyJoinColumn
The #MapKeyJoinColumn requires a third entity, like Manufacturer so that you have an association from Owner to Car and car has also an association to a Manufacturer, so that you can group all Owner's Cars by Manufacturer:
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKeyJoinColumn(name="manufacturer_id")
private Map<Manufacturer, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
#ManyToOne
#JoinColumn(name = "manufacturer_id")
private Manufacturer manufacturer;
}
#Entity
public class Manufacturer {
#Id
private long id;
private String name;
}
Here's a working example of using #MapKey with #OneToMany with a composite #IdClass. It's obviously not the only way to accomplish the objective here, but I felt this was the most maintainable.
#Entity
#Table(name = "template_categories")
#IdClass(TemplateCategoryId.class)
public class TemplateCategory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
long orgId;
#Id
long templateId;
#OneToMany(targetEntity = TemplateEntry.class)
#JoinColumns( {
#JoinColumn(name = "orgId", referencedColumnName = "orgId"),
#JoinColumn(name = "templateId", referencedColumnName = "templateId")
}
)
#MapKey(name="key")
private Map<String, TemplateEntry> keyMap;
source code:
https://github.com/in-the-keyhole/jpa-entity-map-examples/blob/master/src/main/java/com/example/demo/mapkey/entity/TemplateCategory.java