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.
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 using Hibernate 4.3.8.Final and have problem with retrieving #Id property of lazy fetched property: For attached classes calling aidConfiguration.getChipApplication().getId() allways returns null. Other properties, eg. aidConfiguration.getChipApplication().getVersion() returns correctly the value from DB. If chipApplication is not lazy loaded (see the comment in the code), then aidConfiguration.getChipApplication().getId() returns correct non-null value.
What am I dong wrong?
BTW I need it to be lazy.
BaseEntity:
#MappedSuperclass
public class BaseEntity implements Serializable {
#Id
#Column(name = "ID", unique = true)
#Size(min = 1, max = 255)
private String id;
#PrePersist
public final void generateUuid() {
if (this.getId() == null) {
this.setId(UUID.randomUUID().toString());
}
}
public final String getId() {
return id;
}
public final void setId(final String id) {
this.id = id;
}
}
AidConfiguration:
#Entity
#Audited
public class AidConfiguration extends BaseEntity {
#Column
#NotBlank
private String name;
#NotNull
#ManyToOne(fetch = FetchType.LAZY) // if it is EAGER (defaut) then then aidConfiguration.getChipApplication().getId() returns correctly non-null value
private ChipApplication chipApplication;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "aidConfiguration", cascade = CascadeType.ALL) // cascade for auto-saving and deleting items
private List<AidConfigurationItem> aidConfigurationItems;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public ChipApplication getChipApplication() {
return chipApplication;
}
public void setChipApplication(final ChipApplication chipApplication) {
this.chipApplication = chipApplication;
}
public List<AidConfigurationItem> getAidConfigurationItems() {
return aidConfigurationItems;
}
public void setAidConfigurationItems(final List<AidConfigurationItem> aidConfigurationItems) {
this.aidConfigurationItems = aidConfigurationItems;
}
}
ChipApplication:
#Entity
#Audited
public class ChipApplication extends BaseEntity {
#Column
#NotBlank(message = "Aid can not be empty")
private String aid;
#Column
#NotBlank(message = "Product can not be empty")
private String product;
#Column
#NotBlank(message = "Version can not be empty")
private String version;
#NotNull(message = "Network is mandatory")
#ManyToOne(fetch = FetchType.LAZY)
private Network network;
#ManyToMany(fetch = FetchType.EAGER)
private List<AidTag> aidTags;
public String getAid() {
return aid;
}
public void setAid(final String aid) {
this.aid = aid;
}
public String getProduct() {
return product;
}
public void setProduct(final String product) {
this.product = product;
}
public String getVersion() {
return version;
}
public void setVersion(final String version) {
this.version = version;
}
public Network getNetwork() {
return network;
}
public void setNetwork(final Network network) {
this.network = network;
}
public List<AidTag> getAidTags() {
return aidTags;
}
public void setAidTags(final List<AidTag> aidTags) {
this.aidTags = aidTags;
}
}
Bit late, but the issue HH-9588 is still unresolved, and I just had the same issue (XML mapping rather than annotations, though).
Could not get the id from the getter when the binding was lazy. Got it when eager or fetch join.
Fixed it by getting rid of the "final" modifier on the getId() accessor. (final here was an attempt to protect the way primary keys/identifiers are defined in the superclass for all the entities)
before :
public abstract class Foo {
Long id;
public final Long getId() {
return id;
}
protected final void setId( Long id ){
this.id = id;
}
...
after :
public abstract class Foo {
Long id;
// No more final
public Long getId() {
return id;
}
// No more final
protected void setId( Long id ){
this.id = id;
}
...
Now, I can get the Id with a lazy binding as well.
Seems to me that this "final" modifier does not allow Hibernate to proxy this accessor as intended. The other accessors being not "final", one can access their values from the proxy.
So, I wonder whether HH-9588 is really a bug or a misunderstanding of the hibernate ways ?
That seems a bug, if you do not miss anything. I would report it on Hibernate's bug tracking system. It would be nice if you would update this answer afterwards with a link to the bug.
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);
}
}
I have model. there is this part:
model was mapped by jpa annotations.Everywhere I use fetchType = EAGER. If I load vacancy from database, I have 2 duplicates status_for_vacancy objects.
I use property hbm2ddl.auto = update.
If I make new schema of database and fill data, I haven't duplicates status_for_vacancy objects.
It really?
code:
vacancy:
#Entity
#Table(name = "vacancy")
#XmlRootElement(name="vacancy")
public class Vacancy {
private List<VacancyStatus> statusList = new LinkedList<VacancyStatus>();
#OneToMany(mappedBy = "vacancy", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<VacancyStatus> getStatusList() {
return statusList;
}
public void setStatusList(List<VacancyStatus> statusList) {
this.statusList = statusList;
}
}
status_for_vacancy:
#Entity
#Table(name = "status_for_vacancy")
public class StatusForVacancy extends AbstractStatus {
public StatusForVacancy() {
super();
}
public StatusForVacancy(Integer id, String name) {
super(id, name);
}
}
#MappedSuperclass
#XmlRootElement
public abstract class AbstractStatus {
private Integer id;
private String name;
public AbstractStatus() {
super();
}
public AbstractStatus(String name) {
super();
this.name = name;
}
public AbstractStatus(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name ="id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
#NotEmpty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
vacancy_status:
#Entity
#Table(name = "vacancy_status")
public class VacancyStatus extends AbstractHistoryStatus {
private Vacancy vacancy;
private StatusForVacancy status;
public VacancyStatus() {
super();
}
public VacancyStatus(Integer id, User author, Date date,
Vacancy vacancy, StatusForVacancy status) {
super(id, author, date);
this.vacancy = vacancy;
this.status = status;
}
#ManyToOne
#JoinColumn(name = "vacancy_id")
public Vacancy getVacancy() {
return vacancy;
}
public void setVacancy(Vacancy vacancy) {
this.vacancy = vacancy;
}
#ManyToOne
#JoinColumn(name = "status_id")
public StatusForVacancy getStatus() {
return status;
}
public void setStatus(StatusForVacancy status) {
this.status = status;
}
}
#MappedSuperclass
public abstract class AbstractHistoryStatus {
private Integer id;
private User author;
private Date date;
public AbstractHistoryStatus() {
}
public AbstractHistoryStatus(Integer id, User author, Date date) {
super();
this.id = id;
this.author = author;
this.date = date;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
#Column(name="creation_date")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
It is all mapping code for these entities.
in debugger:
both id==500 ==> hibernate understand, that it is same objects.
I try add all data from old database to new database - I get old error(
I fix cause of appearance of this problem. It appearances if I add record to note table:
I highly recommend you write equals() and hashCode() methods. The standard equals()/hashCode() implement referential equality (do 2 objects reference the same memory location). So if hibernate has 2 of the 'same' object in memory, but they don't reference the same memory location then you will see the object show up twice. But if you implement equals() based on primary key being equal, then even if there are two copies of the same object in memory, Hibernate won't give you duplicates.
See the JPA spec:
2.4 Primary Keys and Entity Identity
Every entity must have a primary key. ... The value of its primary key
uniquely identifies an entity instance within a persistence context
and to EntityManager operations
Also see this SO post.