I'm trying to make a many to many association with hibernate (with JPA) but my association table (between Film and Actor) is not created.
My actor class :
#Entity
public class Actor {
private Long id;
private String firstname;
private String lastname;
private int age;
public Actor(){};
public Actor(String firstname, String lastname){
this.firstname=firstname;
this.lastname=lastname;
}
#ManyToMany
#JoinTable(name="actor_films", joinColumns=#JoinColumn(name="actor_id"), inverseJoinColumns=#JoinColumn(name="film_id"))
private Set<Film> films=new HashSet<Film>();
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void addFilm(Film film){
this.films.add(film);
}
}
My Film class :
#Entity
public class Film {
private Long id;
private String title;
#ManyToMany(mappedBy = "films")
#JoinTable(name="actor_films", joinColumns=#JoinColumn(name="film_id"), inverseJoinColumns=#JoinColumn(name="actor_id"))
private Set<Actor> actors=new HashSet<Actor>();
public Film(){};
public Film(String title){
this.title=title;
};
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void addActor(Actor actor){
this.actors.add(actor);
}
}
In my main method I do something like:
// begin transaction
Film film=new Film("DBZ");
Actor actor=new Actor("Bob","A");
em.persist(actor);
em.persist(film);
film.addActor(actor);
// do commit
All tables is created in my hsql database except the association table. So if anyone can help me.
You're mixing field and accessor use for your mapping annotations. You can't do that. In any given class, mapping annotations must be either all on the getters or all on the fields. If you try to mix them, some on the getters and some on the fields, Hibernate will pick getters or fields and ignore the other (I'm not sure if its specified how Hibernate chooses if you don't specify).
Your #Id annotation in each class is on a getter, and that's making Hibernate use your getter-based annotations. Either move the #Id and #GeneratedValue annotations to the id field in each class, or define a getter (and setter) for the actor/film relationship and move the #ManyToMany and #JoinTable annotations to the new getter.
With the annotations on the fields, Hibernate will bypass your getters/setters and access the entity internal fields directly. With them on the getters, Hibernate will call your getters and setters.
Help Hibernate a bit by providing it more information regarding your join table.
In your Film entity
#ManyToMany(mappedBy = "films")
#JoinTable(name="actor_films", joinColumns=#JoinColumn(name="film_id"), inverseJoinColumns=#JoinColumn(name="actor_id"))
Do the same in your Actor entity, but reverse joinColumns and inverseJoinColumns
https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch07.html#collections-bidirectional
Related
I am trying to upgrade my spring boot project from 2.4.3 to 2.5.0. The strange thing happened to me is I am unable to fetch the results when entities are associated.
For example, I have two simple entities:
UserProfile entity:
#Entity
#Table(name = "user_profile")
public class UserProfile {
#Id
private String id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String email;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "tenantId")
private Tenant tenant;
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Tenant entity:
#Entity
public class Tenant {
#Id
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
UserProfile entity has many to one association with Tenant entity. My repository class is
public interface UserProfileRepository extends CrudRepository<UserProfile, String> {
UserProfile findByEmailAndTenantId(String email, String tenantId);
}
Now, the method findByEmailAndTenantId from the above repository always returns null.
The SQLs generated with spring boot 2.5.0 is
select
userprofil0_.id as id1_1_,
userprofil0_.email as email2_1_,
userprofil0_.first_name as first_na3_1_,
userprofil0_.last_name as last_nam4_1_,
userprofil0_.tenant_id as tenant_i5_1_
from
pr.user_profile userprofil0_
inner join
pr.tenant tenant1_
on userprofil0_.tenant_id=tenant1_.id
where
userprofil0_.email=?
and (
tenant1_.id is null
)
The SQL generated for the same entities with spring boot 2.4.3 is
select
userprofil0_.id as id1_1_,
userprofil0_.email as email2_1_,
userprofil0_.first_name as first_na3_1_,
userprofil0_.last_name as last_nam4_1_,
userprofil0_.tenant_id as tenant_i5_1_
from
pr.user_profile userprofil0_
where
userprofil0_.email=?
and (
userprofil0_.tenant_id is null
)
Is this intended behavior in Spring Boot 2.5.0? Can anyone please help me in finding the solution to this problem?
I couldn't find anything in the documentation about it, but read this What is the difference between #ManyToOne(optional=false) vs. #Column(nullable=false)
It seems that optional = false results in INNER JOIN to tenant, that combined with WHERE tenant_id IS NULL can't return any rows.
So if you really need to fetch entities with tenant_id set to null, you've got to remove optional = false from the #JoinColumn annotation.
Consider this regular javabean without ORM/:
// primary key is auto incremented by the database, so I can't add it
public class User {
String name;
int personID; // foreign key
// no args constructor
// getter/setters for fields
}
Is it ok to do this? I personally think it doesn't make sense because there is no reason to manipulate the foreign key through the bean, but I might be wrong. Are there use cases where this is good?
I would normally do it like this.
public class Person {
private final String id;
public Person(String id) {
this.id = id;
}
public Person() {
this.id = UUID.randomUUID().toString();
}
}
and
public class User {
private final String id;
private String personId;
public User(String id, String personId) {
this.id = id;
this.personId = personId;
}
public User() {
this.id = UUID.randomUUID().toString();
}
public String getPersonId() {
return personId;
}
public void setPersonId(String personId) {
this.personId = personId;
}
}
Another alternative for User is to make it immutable. In which case it would look something like this.
public class User {
private final String id;
private final String personId;
public User(String id, String personId) {
this.id = id;
this.personId = personId;
}
public User(String personId) {
this.personId = personId;
this.id = UUID.randomUUID().toString();
}
public String getPersonId() {
return personId;
}
}
Now the classes can be used in an autoincremented or non-autoincremented way. Basically the option is there for the classes to make their own unique ID or for a unique ID to be passed to it.
One common dilemma that I have seen is when the id does not exist yet the object does. That is the case when the object is created in the program but the ID (which maybe created by the DB when it is inserted) is not assigned to it yet. In that case the bean may look like this.
public class User {
private String id;
private final String personId;
public User(String id, String personId) {
this.id = id;
this.personId = personId;
}
public User(String personId) {
this.personId = personId;
}
public String getPersonId() {
return personId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Notice the id is not final and hence the object can exist without the id present and can be added in later if needed
You are better off modelling the objects and their relationships to one another in an object oriented way.
public class User {
private long id;
private Person person;
// .... Removed for clarity
}
I am really newbie to Hibernate and it's been like two hours trying to figure it out how to fix this issue. I am using Hibernate 4 and Postgres 9.3
Given the CatalogBase class
#MappedSuperclass
public class CatalogBase {
#Id
#Type(type = "pg-uuid")
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
protected UUID id;
}
And the derived User class
#Entity
#Table(name="erpuser")
public class User extends CatalogBase {
private String lastName;
private String name;
private String email;
private boolean isSystemAdministrator;
#Type(type="org.hibernate.type.StringClobType")
#Column(nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="lastname")
#Type(type="org.hibernate.type.StringClobType")
#NotNull(message = "es campo mandatorio")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(length = 100,unique = true)
#NotNull(message = "es campo mandatorio")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "issystemadministrator", nullable = false)
public boolean isSystemAdministrator() {
return isSystemAdministrator;
}
public void setSystemAdministrator(boolean isSystemAdministrator) {
this.isSystemAdministrator = isSystemAdministrator;
}
}
I am trying to filter just the first result of a query using Hibernate Criteria. Like this
public boolean existsSystemAdministrator() throws NoSuchAlgorithmException{
Criteria criteria=currentSession()
.createCriteria(User.class)
.add(Restrictions.eq("isSystemAdministrator", true));
return criteria.uniqueResult() != null;
}
But I always get org.hibernate.QueryException: could not resolve property: isSystemAdministrator exception
I have changed to all lowercase since the database field is like that, but it didn't work either. From what I've read Hibernate maps with the Java property, which hasn't been the case as well.
Have tried also change the isSystemAdministrator field to Boolean instead of boolean, but it didn't work out either.
I know this must sound stupid to any Hibernate guru, if someone can come up with an answer that would save me lots of time.
Thanks in advance.
You should adhere to the JavaBeans spec (http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html). The field should be 'systemAdministrator', and the method should be 'isSystemAdministrator'.
The problem is in #Id annotation in CatalogBase class. If you change so it will work fine:
#MappedSuperclass
public class CatalogBase {
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
#Id
#Type(type = "pg-uuid")
protected UUID id;
}
You can have 2 access types in Hibernate. Property access (as you did) or field access. Hibernate will guess the access type from the position of #Id or #EmbeddedId.
As I know (I am not a Hibernate guru), it should be no difference between these two access types. But some frameworks requires to have field access. Anyway, I do not know why your implementation does not work for querying and have not found any other explanation.
I am utterly confused by something I expected to work just out of the box. So either I am doing something totally wrong or this is just a misunderstanding.
I am trying to have a getter/setter annotation in a JPA Entity class. I sticked to an example I found on the JPA wiki (s. http://en.wikibooks.org/wiki/Java_Persistence/Basic_Attributes#Conversion). The example looks as follows:
#Entity
public class Employee {
...
private boolean isActive;
...
#Transient
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
#Basic
private String getIsActiveValue() {
if (isActive) {
return "T";
} else {
return "F";
}
}
private void setIsActiveValue(String isActive) {
this.isActive = "T".equals(isActive);
}
}
I took the clearest and cleanest spring-data-jpa example I could find: http://spring.io/guides/gs/accessing-data-jpa/.
I checked it out from git and changed their example entity class (s. https://github.com/spring-guides/gs-accessing-data-jpa/blob/master/complete/src/main/java/hello/Customer.java) to look as follows:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
#Transient
private boolean isActive;
#Transient
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
#Column
private String getIsActiveValue() {
if (isActive) {
return "T";
} else {
return "F";
}
}
private void setIsActiveValue(String isActive) {
this.isActive = "T".equals(isActive);
}
}
Now nothing changes. The respective String-Field does not get created. The line from the log creating the database table still looks as follows:
17:11:10.540 [main] DEBUG o.h.tool.hbm2ddl.SchemaUpdate - create table Customer (id bigint generated by default as identity, firstName varchar(255), lastName varchar(255), primary key (id))
I have absolutely no idea on what could be the reason for this. I could find not documentation that spring-data-jpa would not allow for annotations on getters.
Any help would be very, very appreciated!
I think you simply mixed the annotations: you must either annotate the fields, or the getters, but not both. Once you decided to annotate your ID field, you must annotate all the fields (and not getters), and the opposite: if you annotated your getId() method, that you must annotate all methods.
If you want it to work with the '#Transient' annotation you should do as Andrei suggests, you could add an extra field for isActiveValue but it is most important to annotate consistently otherwise you will get unpredictable behavior.
When annotating fields versus properties (getters and setters) it will make a difference.
In your case it looks like you want to do some logic in the getter hence annotating a field will likely not have the desired result. I don't particularly like the logic but understand that there is a need to annotate a getter.
Considering the logic in your code above I would simply eliminate the transient on the field altogether and put the logic with the annotations in the getters and setters.
#Entity
#Table(name = "Customer")
public class Customer {
private static final String IS_ACTIVE = "T";
private long id;
private String firstName;
private String lastName;
private String isActive = "";
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Column(unique = true, nullable = false)
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Column(unique = true, nullable = false)
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(unique = true, nullable = false)
public String getIsActive() {
return isActive;
}
public void setIsActive(String isActive) {
this.isActive = isActive;
}
#Transient
public boolean isActive() {
return isActive.equals(IS_ACTIVE);
}
#Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}
It sounds strange. But i need to validate the field Adress of Company in Person entity.
Company is a field of Person and Addres is afield of Company.
I want to validate the address of Company in Person entity.
Why?? Because The company should have an address if i use it in a Person entity not before?
How can I implement this validation??
#Entity
#Table(schema = Constants.DB_SCHEMA)
public class Person{
Company company;
// Validation of the address should come here ???
public Company getCompany() {
return company;
}
}
#Entity
#Table(schema = Constants.DB_SCHEMA)
public class Company{
String address;
}
Basically you can do that with the annotation #NotNull on the address attribute of your Company class.
But: Your current code won't work anyway because you have not specified the relation between the classes and you don't have primary keys....
Here is an example how it could work:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#OneToOne(cascade= CascadeType.ALL)
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
and
#Entity
public class Company {
#Id
#GeneratedValue
private Long id;
#NotNull
private String adress;
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}