Khám phá nguyên nhân và cách khắc phục lỗi Hibernate AnnotationException. Hướng dẫn chi tiết giúp bạn xử lý lỗi annotation hiệu quả trong Hibernate.
Gần đây, khi tôi làm việc trên một dự án Hibernate và thêm một vài entity bean, lúc chạy chương trình tôi đã gặp thông báo exception stack trace như dưới đây.
Initial SessionFactory creation failed.org.hibernate.AnnotationException: No identifier specified for entity: com.journaldev.hibernate.model.Address
org.hibernate.AnnotationException: No identifier specified for entity: com.journaldev.hibernate.model.Address
at org.hibernate.cfg.InheritanceState.determineDefaultAccessType(InheritanceState.java:277)
at org.hibernate.cfg.InheritanceState.getElementsToProcess(InheritanceState.java:224)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:775)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3788)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3742)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1410)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
at com.journaldev.hibernate.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:22)
at com.journaldev.hibernate.util.HibernateUtil.getSessionFactory(HibernateUtil.java:34)
at com.journaldev.hibernate.main.HibernateExample.main(HibernateExample.java:15)
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.journaldev.hibernate.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:29)
at com.journaldev.hibernate.util.HibernateUtil.getSessionFactory(HibernateUtil.java:34)
at com.journaldev.hibernate.main.HibernateExample.main(HibernateExample.java:15)
Nguyên nhân dẫn đến lỗi trên là do tôi quên khai báo primary key trong Entity bean. Bean của tôi được định nghĩa như sau: Address.java
package com.journaldev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
@Entity
@Table(name = "ADDRESS")
@Access(value=AccessType.FIELD)
public class Address {
@Column(name = "emp_id", unique = true, nullable = false)
@GeneratedValue(generator = "gen")
@GenericGenerator(name = "gen", strategy = "foreign", parameters = { @Parameter(name = "property", value = "employee") })
private long id;
@Column(name = "address_line1")
private String addressLine1;
@Column(name = "zipcode")
private String zipcode;
@Column(name = "city")
private String city;
@OneToOne
@PrimaryKeyJoinColumn
private Employee employee;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
@Override
public String toString() {
return "AddressLine1= " + addressLine1 + ", City=" + city
+ ", Zipcode=" + zipcode;
}
}
Tất cả những gì mình cần làm để khắc phục lỗi là thêm annotation @Id
cho trường primary key. Mình đã sửa phần khai báo trường id như dưới đây và lỗi đã được giải quyết:
@Id
@Column(name = "emp_id", unique = true, nullable = false)
@GeneratedValue(generator = "gen")
@GenericGenerator(name = "gen", strategy = "foreign", parameters = { @Parameter(name = "property", value = "employee") })
private long id;
Đây là một lỗi dễ khắc phục, nhưng có một trường hợp khiến nó trở nên rắc rối hơn. Thông thường, Hibernate sẽ tự động xác định cách truy cập các thuộc tính của bean dựa trên việc bạn đặt annotation ở biến hoặc getter/setter. Tuy nhiên, chúng ta cũng có thể chỉ định rõ ràng kiểu truy cập cho entity bean. Có hai kiểu truy cập chính:
- Field: Hibernate sẽ tìm kiếm chú thích trên các biến trong trường hợp này, giống như chúng ta đã định nghĩa cho lớp Địa chỉ ở trên là
@Access(value=AccessType.FIELD)
. - Property: Hibernate sẽ tìm kiếm chú thích trên các phương thức getter-setter trong trường hợp này, cú pháp cho việc này là
@Access(value=AccessType.PROPERTY)
Nếu các annotation không được khai báo đúng với kiểu truy cập đã định, bạn cũng sẽ gặp exception này. Ví dụ: nếu kiểu truy cập là Property nhưng tất cả annotation lại đặt trên biến, Hibernate sẽ báo lỗi. Dưới đây là một ví dụ class Employee.java
sẽ gây ra exception này.
package com.journaldev.hibernate.model;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Cascade;
@Entity
@Table(name = "EMPLOYEE")
@Access(value=AccessType.FIELD)
public class Employee {
private long id;
private String name;
private double salary;
private Address address;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@OneToOne(mappedBy = "employee")
@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Column(name = "emp_name")
public String getName() {
System.out.println("Employee getName called");
return name;
}
public void setName(String name) {
System.out.println("Employee setName called");
this.name = name;
}
@Column(name = "emp_salary")
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Id= " + id + ", Name= " + name + ", Salary= " + salary
+ ", {Address= " + address + "}";
}
}
Hãy để ý rằng tất cả các annotation JPA đều được đặt trên các phương thức getter, trong khi access type lại được khai báo là Field. Chỉ cần đổi access type thành Property là sẽ giải quyết được vấn đề này.