I need a link between two entities, so I use a one-to-one
#Entity
#Table(name = "T_USER")
public class User implements Serializable {
#Id
#Column(name = "user_id")
private int userId;
#Column(name = "login")
private String login;
#OneToOne(optional = true)
#JoinColumn(name="login", referencedColumnName="person_id", nullable = true, insertable = false, updatable = false)
private Person person;
}
#Entity
#Table(name = "T_PERSON")
public class Person implements Serializable {
#Id
#Column(name = "person_id")
private String personId;
#Column(name = "pin")
private String pin;
}
If there is no item for a particulary PERSON in table T_USER, user.getPerson throw a exception:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [packagename.com.entity.Person#scabriou]
But If I have reference between the 2 tables in the db, the getter works!
I can't say if this the best solution but you could use the #NotFound annotation. E.g.
#NotFound(action = NotFoundAction.IGNORE)
private Person person;
I believe person will remain null and the exception will not be thrown.
Related
I have already a user model.
Now I have created a movie model, my requirement is that whenever any existing user is going to add any movie, at that time user_id and movie_id will be store in the movie_added_by table.
Here user model needs to map one to many to movie_added_by and similarly, the movie will be mapped to movie_added_by.
For better understanding, you can refer to the DB diagram.
I really don't know how can I do by using hibernate annotation
The user model is like this:
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
}
The movie model is like this:
#Getter
#Setter
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
}
You probably want to create a #ManyToMany relationship between the entities. There are 2 ways of doing it (with intermediary table created explicitly or by Hibernate.
In simple approach your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#ManyToMany(cascade = CascadeType.Persist)
#JoinTable(name="user_movie",
joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="movie_id)})
private Set<Movie> movies = new HashSet<>();
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#ManyToMany(cascade = CascadeType.Persist, mappedBy = "movies" //field from the user class responsible for mapping)
private Set<User> users = new HashSet<>()
}
So basically here you tell Hibernate to create an intermediary table and keep there correlated id's of those 2 entities. Couple of other notes here:
a) you might want to change the id variable type from Integer to Long in case your entities grow;
b) If you have annotated a column with #Id, you don't have to use unique=true and nullable = false in the column annotation;
c) remember about implementing no-args constructor;
d) remember to exclude relationship fileds from the equals(), hashCode() and the toString() methods;
There is another way, where you explicitly create a model for the table keeping relationships. This might become handy, when it turns out that You need to keep more data in the 'relationship table'. In that case, Your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#OnetToMany(cascade = CascadeType.PERSIST, mappedBy = "user")
private Set<AddedMovie> addedMovies = new HashSet<>()
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "movie")
private Set<AddedMovie> moviesAddedByUser = new HashSet<>();
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity
public class AddedMovie{
#Id
#GeneratedValue
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "movie_id")
private Movie movie;
// sine this entity has now its own lifecycle, you can add more fields here
private Integer rating;
private LocalDateTime movieAddedOn;
}
Greetings to the community,
I am struggling all day to find a solution to the issue below.
The scenario is the following, I have a table
---TABLE_ONE---
INT ID
VARCHAR NAME
PRIMARY_KEY (ID)
and my other table consisted of three columns which consist together a composite key
---TABLE_TWO---
INT TABLE_ONE_ID (FK -> TABLE_ONE.ID)
VARCHAR NAME
VARCHAR EMAIL
PRIMARY_KEY(TABLE_ONE_ID, NAME, EMAIL)
The relationship I want to achieve is that the TABLE_ONE entity will
have a list of objects from the TABLE_TWO (one-to-many relationship).
I tried to do this with as shown below.
#Entity
#Table(name = "TABLE_ONE")
public class TableOne {
#Column(name="id")
private int id;
#Column(name="name")
private String name
#OneToMany(fetch = FetchType.EAGER, mappedBy = "tableOne")
private List<TableTwo> tableTwoList;
//getters, setters, constructors
}
#Entity
#Table(name = "TABLE_TWO")
public class TableTwo {
#EmbeddedId
private TableTwoCompositeId tableTwoCompositeId;
#ManyToOne
#JoinColumn(name = "TABLE_ONE_ID", referencedColumnName = "ID", insertable = false, updatable = false)
private TableOne tableOne;
//getters, setters, constructors
}
#Embeddable
public class TableTwoCompositeId {
#Column(name = "TABLE_ONE_ID")
public Integer provider;
#Column(name = "NAME")
public String name;
#Column(name = "EMAIL")
public String email;
//getters, setters, constructors
}
However, I'm getting javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet and Caused by: java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist when a TableOne object is retrieved from the database.
Thanks in advance for any help!
I think you need several minor changes:
TableOne.id needs an #Id annotation
The type of TableTwoCompositeId.provider should match the type of TableOne.id
TableTwo.tableOne needs a #MapsId annotation to indicate it maps TableTwoCompositeId.provider
Here is how the code should look:
#Entity
#Table(name = "TABLE_ONE")
public class TableOne {
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name
#OneToMany(fetch = FetchType.EAGER, mappedBy = "tableOne")
private List<TableTwo> tableTwoList;
//getters, setters, constructors
}
#Entity
#Table(name = "TABLE_TWO")
public class TableTwo {
#EmbeddedId
private TableTwoCompositeId tableTwoCompositeId;
#MapsId("provider") // maps provider attribute of embedded id
#ManyToOne
#JoinColumn(name = "TABLE_ONE_ID", referencedColumnName = "ID", insertable = false, updatable = false)
private TableOne tableOne;
//getters, setters, constructors
}
#Embeddable
public class TableTwoCompositeId {
#Column(name = "TABLE_ONE_ID")
public int provider;
#Column(name = "NAME")
public String name;
#Column(name = "EMAIL")
public String email;
//getters, setters, constructors
}
I have main table merchants and second table terminals:
Merchant table:
#Entity
#Table
public class Merchants {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private int id;
#Column
private String name;
#Column
private String login;
}
Terminal table:
#Entity
#Table
public class Terminals {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private int id;
#Column
private int merchant_id;
#Column
private String mode;
}
I want to set merchant_id into table Terminals. I suppose that many to many will be proper relation. How I can create it?
If you have a Join Table:
On Merchants class:
#ManyToMany
#JoinTable(name="MER_TER", joinColumns=#JoinColumn(name="MERCH_ID", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="TERM_ID", referencedColumnName="id"))
private List<Terminals> terminalsList;
On Terminals class:
#ManyToMany(mappedBy="terminalsList")
private List<Merchants> merchantsList;
Page of reference: link
If you don't have a Join Table, try to look here: https://stackoverflow.com/a/25018992
I have tried to create some JPA Entities for a DB designed with the following tables: PRINCIPALS and CREDENTIALS which have the following relations with other tables:
#Entity
#Table(name = "CREDENTIALS")
public class Credentials {
#Id
#Column(name = "CREDENTIAL_ID")
private Integer credentialID;
#Id
#Column(name = "CREDENTIAL_TYPE_ID")
private String credentialTypeID;
#OneToOne
#JoinColumn(name = "CREDENTIAL_TYPE_ID", insertable = false, updatable = false)
private CredentialTypes credentialTypes;
}
CREDENTIALS has a oneToOne relation with CREDENTIAL_TYPES
#Entity
#Table(name = "CREDENTIAL_TYPES")
public class CredentialTypes {
#Id
#Column(name = "CREDENTIAL_TYPE_ID")
private String credentialTypeID;
#Column(name = "DESCRIPTION")
private String description;
}
#Entity
#Table(name = "PRINCIPALS")
public class Principals implements Serializable {
#Id
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Column(name = "PRINCIPAL_ID", nullable = false)
private String principalID;
#OneToOne
#JoinColumn(name = "PRINCIPAL_TYPE_ID", insertable = false, updatable = false)
private PrincipalTypes principalTypes;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "PRINCIPAL_CREDENTIAL",
joinColumns = #JoinColumn(name = "CREDENTIAL_ID"),
inverseJoinColumns = #JoinColumn(name = "PRINCIPAL_ID"))
private List<Credentials> credentials;
PRINCIPALS has a oneToOne relation with PRINCIPAL_TYPES
#Entity
#Table(name = "PRINCIPAL_TYPES")
public class PrincipalTypes implements Serializable {
#Id
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Column(name = "DESCRIPTION")
private String description;
And finally PRINCIPALS has a oneToMany relation with CREDENTIALS and uses a join table PRINCIPLE_CREDENTIAL
#Entity
#Table(name = "PRINCIPAL_CREDENTIAL")
public class PrincipalCredential implements Serializable {
#Id
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Id
#Column(name = "PRINCIPAL_ID", nullable = false)
private String principalID;
#Id
#Column(name = "CREDENTIAL_ID")
private Integer credentialID;
#Id
#Column(name = "CREDENTIAL_TYPE_ID")
private String credentialTypeID;
At startup (using SpringBoot) I receive an error for the oneToMany relation between Principals and Credentials and just don't have any idea how to fix it... Tried various other methods (The DB design cannot be changed).
Caused by: org.hibernate.AnnotationException: A Foreign key refering entities.Principals from entities.Credentials has the wrong number of column. should be 2
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:502)
at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1467)
at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1233)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:794)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:729)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:70)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1697)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:85
I find the exception wierd because there is no refering of Principlas from Credentials....
PRINCIPLE_TYPE_ID and CREDENTIAL_TYPE_ID are missing in the joinColumns/inverseJoinColumns. I think you must use the #JoinColumns Annotation
I have an entity (PersonQuestionsEntity) that has a PersonEntity and QuestionEntity as its primary key. I use a composite key to reflect this relationship.
Now, I want to create a Criteria object that can do the following: Find all PersonQuestion entities for a given questionId and a person's age.
Here is my attempt at creating a Criteria for this:
Session session = getHibernateTemplate().getSessionFactory().openSession();
Criteria criteria = session.createCriteria(PersonQuestionsEntity.class);
criteria.add(Restrictions.eq("question.questionId", "87"));
criteria = criteria.createCriteria("person");
criteria.add(Restrictions.eq("age", 23));
criteria.setMaxResults(100);
List l = criteria.list();
The problem is that I get this error:
Caused by: java.sql.SQLException: ORA-00904: "PERSONENTI1_"."AGE": invalid identifier
In the generated SQL, it seems the person is referenced as PERSONENTI4, not PERSONENTI1. If I copy the SQL and run it with PERSONENTIT4 instead of PERSONENTI4, it works (sort of -- it seems to be doing a cartesian join of some sort).
Any clues as to what I might be doing wrong? I'm very new to using Hibernate.
PersonQuestionsEntity
#Entity
#IdClass(com.anonymous.model.PersonQuestionsKey.class)
#Table(name = "PERSON_QUESTIONS")
public class PersonQuestionsEntity implements Serializable
{
private static final long serialVersionUID = -8254277382097937813L;
#Id
#ManyToOne
#JoinColumn(name = "USER_NAME", nullable = false)
private PersonEntity person;
#Id
#ManyToOne
#JoinColumn(name = "QUESTION_ID", nullable = false)
private QuestionEntity question;
#Column(name = "THEIR_ANSWER")
private int theirAnswer;
}
PersonEntity
#Entity
#Table(name = "PERSON")
public class PersonEntity implements Serializable
{
private static final long serialVersionUID = -1699435979266209440L;
#Id
#Column(name = "USER_NAME", length = 20, nullable = false)
private String userName;
#Column(name = "USER_NAME_REAL", length = 20, nullable = false)
private String userNameReal;
#Column(name = "AGE", nullable = false)
private int age;
}
PersonQuestionsKey
#Embeddable
public class PersonQuestionsKey implements Serializable
{
private static final long serialVersionUID = -264160855961369405L;
#Id
#ManyToOne
#JoinColumn(name = "USER_NAME", nullable = false)
private PersonEntity person;
#Id
#ManyToOne
#JoinColumn(name = "QUESTION_ID", nullable = false)
private QuestionEntity question;
}
First of all, you don't really need the inner criteria, simply use:
Criteria criteria = session.createCriteria(PersonQuestionsEntity.class);
criteria.add(Restrictions.eq("question.questionId", "87"));
criteria.add(Restrictions.eq("person.age", 23));
criteria.setMaxResults(100);
List l = criteria.list();
Second (regarding the join type), in such cases, I usually go with HQL that produces inner joins. The HQL might look as follows:
from PersonQeustionEntity where question.questionId = :questionId
and person.age = :age
in the result Query object you can set the parameters questionId and age to your desired inputs.