Understanding onetomany and manytoone JPA - java

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.

Related

encounter java.lang.StackOverflowError when used #OneToOne for select in Hibernate

when I test some things about mapping relationship of #OneToOne in Hibernate, and I use spring-data-jpa to query. For the bidirectional relationship of the #OneToOne, when I query an entity, it will occurred two conditions:
when no data in two tables, no errors;
when data is stored in two tables, just StackOverflowError;
the related code in the next:
#Entity
public class Person {
#Id
#GeneratedValue
private Integer personId;
private String name;
#OneToOne
private IdCard idCard;
// setter&getter
}
#Entity
public class IdCard {
#Id
#GeneratedValue
private Integer number;
private String area;
#OneToOne(mappedBy="idCard")
private Person person;
}
PersonDao:
#Transactional
#Repository
public interface PersonDao extends CrudRepository<Person, Integer> {
public Person findByPersonId(Integer personId);
}
IdCardDao:
#Transactional
#Repository
public interface IdCardDao extends CrudRepository<IdCard, Integer> {
public IdCard findByNumber (Integer number);
}
test code:
Person person = personDao.findByPersonId(1);
System.out.println(person);
IdCard idCard = idCardDao.findByNumber(123);
System.out.println(idCard);
I search some answers in the website, find a related question, StackOverFlowError while doing a One-To-One relationship in GAE Datastore using JPA 2.0
but I did not instantiate the entity explicitly, so no recurses. use jpa 2.1
Any solutions?
I had the same problem in #OneToOne relationship, and the solution for the java.lang.StackOverflowError: null is to use fetch LAZY in the owing side of the relationship.
#Entity
public class Person {
#Id
#GeneratedValue
private Integer personId;
private String name;
#OneToOne (fetch = FetchType.LAZY)
private IdCard idCard;
// setter&getter
}
#Entity
public class IdCard {
#Id
#GeneratedValue
private Integer number;
private String area;
#OneToOne(mappedBy="idCard")
private Person person;
}
Try like this, you can map with one end., for OneToOne and ManyToOne at the receiving end you don't need to mention,
#Entity
public class Person {
#Id
#GeneratedValue
private Integer personId;
private String name;
#OneToOne
private IdCard idCard;
// setter&getter
}
#Entity
public class IdCard {
#Id
#GeneratedValue
private Integer number;
private String area;
private Person person;
}

How to convert this UML to JPA entities

I have this UML diagram.
And I tried to build entities like this (I renamed Entity class to Entidad)
RelationshipType.java
#Entity
#Table(name = "relationship_type")
public class RelationshipType {
#Id
#GeneratedValue
private Long id;
private String type;
#OneToMany(mappedBy = "relationshipType", fetch = FetchType.EAGER)
private Set<Relationship> relationships = new HashSet<Relationship>();
//Getters and Setters
Relationship.java
#Entity
#Table(name = "relationship")
public class Relationship {
#Id
#ManyToOne
private RelationshipType relationshipType;
#Id
#ManyToOne
private Entidad entity;
//Getters and Setters
Entidad.java
#Entity
#Table(name = "entity")
public class Entidad {
#Id
#GeneratedValue
private Long id;
private String image;
private String foundationNotes;
private String alias;
private Boolean excludeNotifications;
private String notes;
//[...]
#ManyToOne
private Relationship related;
#OneToMany(mappedBy = "entity", fetch = FetchType.EAGER)
private Set<Relationship> relationships = new HashSet<Relationship>();
But when I launch app throws this:
Foreign key (FK_9d8afoh1pv9r59iwjkbcpnud1:entity [])) must have same number of columns as the referenced primary key (relationship [relationshipType_id,entity_id])
At now, I don't know where is the problem and need do this well because I'm using this entities to build the DB schema.

One-To-Many Hibernate Entity Mapping : ids for this class must be manually assigned before calling save()

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;
}

Unique constraint in OneToOne bi-directional mapping

Is unique=true required in bi-directional one-to-one mapping in the owner of the relationship?
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#OneToOne(cascade={CascadeType.PERSIST})
#JoinColumn(name="passport_id", unique=true) //is unique=true required for bi-directional one-to-one mapping
private Passport passport;
public Passport getPassport() {
return passport;
}
}
#Entity
public class Passport {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="passport_number")
private String passportNumber;
#OneToOne(mappedBy="passport")
private Customer customer;
public Customer getCustomer() {
return customer;
}
}
Hibernate documentation says that FK column in the database should be constrained unique to simulate one-to-one multiplicity, but it doesn't add the unique=true in the bi-directional mapping.
That's because it's not mandatory to use the Hibernate auto DDL feature. You can have incremental schema update scripts and the schema related annotations would be useless. Frankly, we kinda use those for in memory integration testing.
As you pointed out, the JoinColumn should state the uniqueness constraint.

Hibernate: mapping #OneToMany / #ManyToOne problems (not using entity primary keys)

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;

Categories

Resources