[Hibernate] List 映射例子
List 是 java 集合的一个工具,存储线性的数据,允许重复数据。开发者可以准确控制在 list 那个位置插入数据。本例子演示 Java 的 List 集合和 MySQL 数据库的映射应用。
使用场景:一个员工有一个或多个证书,对员工以及其拥有的证书进行增删该查操作。这个一个 1 : n的场景。
代码的目录结构如下:
hibernate.cfg.xml 存储数据库信息,如数据库类型,账号密码,数据库名称。
Empoyee.hbm.xml,声明 Employee 对象及其属性和数据库表结构的对应关系。其中,Employee 的属性包括一个 Certificate 的 List 集合。后面详细介绍此文件。
Employee.java,Employee 实体类。
Certificate.java, Certificate 实体类。
ManageEmployee.java,管理 Employee,并对外提供操作 Employee 对象数据的接口。
App.java,演示本例子。
代码详情
EMPLOYEE, CERTIFICATE 在 MySQL 中的建表语句。
虽然 EMPLOYEE 和 CERTIFICATE 是一对多的关系,在逻辑上 CERTIFICATE.employee_id 指向 EMPLOYEE.id,但是并没用使用外键进行约束,而是通过代码逻辑进行约束。这样的结构更灵活些。
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
create table CERTIFICATE ( id INT NOT NULL auto_increment, certificate_name VARCHAR(30) default NULL, idx INT default NULL, employee_id INT default NULL, PRIMARY KEY (id) );
Certificate.java 。在数据库中 CERTIFICATE 有四个属性,其中 idex 表示证书在 List 中的位置,employee_id 表示归属哪一个员工,这两个数据在 Employee.hbm.xml 中声明,有 hibernate 自动填充。所以,只需要在 Certificate.java 中声明 id, name 属性就可以了。
package tony.hibernateList; public class Certificate { private int id; private String name; public Certificate(){} public Certificate(String name){ this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Employee.java,除了四个基本类型的属性外,还有一个集合属性 List<Certificate> certificates。
package tony.hibernateList; import java.util.List; public class Employee { private int id; private String firstName; private String lastName; private int salary; private List<Certificate> certificates; public Employee(){} public Employee(String fName, String lName, int salary){ this.firstName = fName; this.lastName = lName; this.salary = salary; } public int getId() { return id; } public void setId(int 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 getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public List<Certificate> getCertificates() { return certificates; } public void setCertificates(List<Certificate> certificates) { this.certificates = certificates; } }
Employee.hbm.xml,声明了 Employee 类和数据库 EMPLOYEE 表的映射关系。hbm.xml 虽然作为 java 对象和数据库表的中间层,但是和 java 对象相似些。
Class 元素是 java 类和数据库表的映射关系,其中 name 是 java 的全路径类名,table 是 MySQL 表名。
id 元素是 java 类中唯一 ID 属性和数据库表中主键的映射,其中 name 是 java 属性名称,column 是数据库表的列名称,type 是 hibernate 映射类,用于转换 java 和 SQL 数据。
property 元素是 java 属性和数据库表的列名称的映射。其中 name, column type 的作用和 id 元素的相似。
list 元素用于声明 Employee 元素和 Certificate 之间的关系。
cascade 表示在保存 employee 到数据库的同时,也要保存其关联的 certificates 。
key 声明指向父类的外键。
list-index 存储当前证书在其集合中的位置。
one-to-many 表示这是一对多的情况。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="tony.hibernateList.Employee" table="EMPLOYEE"> <id name="id" type="int" column="id"> <generator class="native"/> </id> <list name="certificates" cascade="all"> <key column="employee_id"/> <list-index column="idx"/> <one-to-many class="tony.hibernateList.Certificate"/> </list> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class> <class name="tony.hibernateList.Certificate" table="CERTIFICATE"> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="name" column="certificate_name" type="string"/> </class> </hibernate-mapping>
ManageEmployee.java 是 Employee 及其 Certificate 的操作类。对业务层提供操作 Employee 的接口。仅在 addEmployee 方法做了异常检测和失败回滚处理,其他方法省略,实际代码应加上。
package tony.hibernateList; import java.util.List; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; public class ManageEmployee { private static SessionFactory factory; { factory = new Configuration().configure().buildSessionFactory(); } /** * Add employee and certificate with transaction. Roll back if insert failed * @param fname * @param lname * @param salary * @param cert * @return */ public Integer addEmployee(String fname, String lname, int salary, List<Certificate> cert){ Session session = factory.openSession(); Transaction tx = null; Integer employeeID = null; try{ tx = session.beginTransaction(); Employee employee = new Employee(fname, lname, salary); employee.setCertificates(cert); employeeID = (Integer)session.save(employee); tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } return employeeID; } public void listEmployee(){ Session session = factory.openSession(); Transaction tx = null; tx = session.beginTransaction(); List<Employee> emps = session.createQuery("FROM Employee").list(); for (Employee emp : emps){ System.out.println(emp.getId() + ", " + emp.getFirstName() + ", " + emp.getLastName() + ", " + emp.getSalary()); List<Certificate> certs = emp.getCertificates(); for (Certificate cert : certs){ System.out.println(cert.getId() + " | " + cert.getName()); } } tx.commit(); session.close(); } public List<Employee> getAllEmployee(){ Session session = factory.openSession(); Transaction tx = null; tx = session.beginTransaction(); List<Employee> emps = session.createQuery("FROM Employee").list(); tx.commit(); session.close(); return emps; } public void updateEmployee(int employeeId, int salary){ Session session = factory.openSession(); Transaction tx = null; tx = session.beginTransaction(); Employee emp = session.get(Employee.class, employeeId); emp.setSalary(salary); session.update(emp); tx.commit(); session.close(); } public void deleteEmployee(int employeeId){ Session session = factory.openSession(); Transaction tx = null; tx = session.beginTransaction(); Employee employee = session.get(Employee.class, employeeId); session.delete(employee); tx.commit(); session.close(); } }
App.java 应用入口类,演示本例子。
package tony.hibernateList; import java.util.ArrayList; import java.util.List; public class App { public static void main( String[] args ) { ManageEmployee me = new ManageEmployee(); ArrayList<Certificate> cert = new ArrayList<>(); cert.add(new Certificate("MCA")); cert.add(new Certificate("MBA")); cert.add(new Certificate("PMP")); Integer empID1 = me.addEmployee("Manoj", "Kumar", 4000, cert); System.out.println(empID1); cert = new ArrayList<>(); cert.add(new Certificate("HHH")); cert.add(new Certificate("BBB")); cert.add(new Certificate("CCC")); Integer empID2 = me.addEmployee("TTT", "FFF", 4000, cert); System.out.println(empID2); me.listEmployee(); List<Employee> employees = me.getAllEmployee(); Employee last2emp = employees.get(employees.size() - 2); me.deleteEmployee(last2emp.getId()); System.out.println("End"); } }
hibernate.cfg.xml,存储数据库信息。打开 show_sql 选项,可以输出最终执行的 SQL 语句。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- Assume test is the database name --> <property name="hibernate.connection.url"> jdbc:mysql://localhost/hibernateTest </property> <property name="hibernate.connection.username"> username </property> <property name="hibernate.connection.password"> password </property> <property name="show_sql">true</property> <!-- List of XML mapping files --> <mapping resource="Employee.hbm.xml" /> </session-factory> </hibernate-configuration>
参考资料
Hibernate - List Mappings, tutorialspoint