注意:
使用集合属性时,一定要使用接口,而不能声明为具体的实现类。
因为经过Session操作后,集合就变成了Hibernate自己的集合实现类。
另外:无序集合是可以排序的sort属性默认为unsorted;sort="unsorted|natural|comparatorClass"
:指定排序,这是在内存中排序,效率较低,一般不使用
order-by属性:写的是order by 子句,是SQL语句,是操作的集合表。
这是在查询数据时指定orderby子句,效率高,经常使用。
lazy建议为false对集合属性设置懒加载,这样可以减少查询数据的次数,只有在需要集合元素时才会真正查询数据库
Session常用方法统讲,以及hibernate中对象状态的改变:
============================
对象的状态:
临时状态:
与数据库没有对应,跟Session没有关联。
一般是新new出的对象。
持久化状态:
对象在Session的管理之中,最终会有对应的数据库记录。
特点:
1,有OID
2,对对象的修改会同步到数据库。
游离状态:
数据库中有对应记录,但对象不在Session管理之中。
修改此状态对象时数据库不会有变化。
删除状态:
执行了delete()后的对象。
===============================
一、操作实体对象的
save():保存对象
update()
// update():把游离状态变为持久化状态
// 会生成:update ...
// 在更新时,对象不存在就报错
saveOrUpdate()
// saveOrUpdate():把临时或游离状态转为持久化状态
// 会生成:insert into 或 update ...
// 在更新时,对象不存在就报错
// 本方法是根据id判断对象是什么状态的:如果id为原始值(对象的是null,原始类型数字是0)就是临时状态,如果不是原始值就是游离状态。
delete()
// delete():把持久化或游离转为删除状态
// 会生成:delete ...
// 如果删除的对象不存在,就会抛异常
二、操作缓存的
clear():把session中所有对象从持久态变成游离态
evict():把session中单个对象从持久态变成游离态
flush()
// 操作大量数据,要防止Session中对象过多而内存溢出
所以我们要在内存之前执行
session.flush();
session.clear();
先把session中数据同步到数据库中,然后清空session缓存
三、查询实体对象的
get()
load()
createQuery()://hql语句
createCriteria():面向对象方式的
// load():获取数据,是持久化状态
// 会生成:select ... where id=?
// load()后返回的是一个代理对象,要求类不能是final的,否则不能生成子类代理,就不能使用懒加载功能了。
// 让懒加载失效的方式:一、把实体写成final的;二、在hbm.xml中写<class ... lazy="false">
// 不会马上执行sql语句,而是在第1次使用非id或class属性时执行sql。
// 如果数据不存在,就抛异常:ObjectNotFoundException
加载方式 返回值如果数据不存在
---------------------------------------------------------
get 立即加载 真实对象或null 返回null
load 延迟加载代理对象 抛异常
hibernate映射关系:
双向关联
单向关联:
单向多对一
单向一对多
单向多对多
一对一
单向在这里不在赘述,只需在双向的基础上修改配置即可:
讲解介绍都在代码中:
现实世界的关系映射大概是三种,一对一,一对多,多对多,单向和双向映射,这里只介绍双向,单向只需在双向的基础上修改即可:
数据库连接配置清单:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///test</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 指定方言,指定mysql ,建议配置成上面,不然hibernate在执行创建表的时候会报错,目前没有找到原因--> <property name="hbm2ddl.auto">update</property><!-- -这个参数指定,hibernate可以更新数据,当为create时,hibernate每次执行插入操作都会先删除表,然后在创建表插入数据 --> <property name="show_sql">true</property><!-- -这个参数,表示,hibernate会把自动生成的参数,显示在控制台给我们看,一般用于开发阶段 --> <!-- <mapping resource="com/leige/domain/User.hbm.xml"/> --><!-- -指定映射文件,告诉hibernate,对象与表的映射关系 --> </session-factory> </hibernate-configuration>
一:一对多映射(这里采用的事经典的员工部门例子):
部门类(Department)和部门表的(Department.hbm.xml)xml配置文件:
Department:
package hibernate_oneToMany; import java.util.HashSet; import java.util.Set; public class Department { private Integer id; private String name; private Set<Employee> employees=new HashSet<Employee>(); public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Employee> getEmployees() { return employees; } public void setEmployees(Set<Employee> employees) { this.employees = employees; } @Override public String toString() { return "Department [id=" + id + ", name=" + name + "]"; } }Department.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2016-4-2 13:39:24 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping package="hibernate_oneToMany"> <class name="Department" table="department"> <id name="id" type="int"> <column name="id" /> <generator class="native" /> </id> <property name="name" type="string"> <column name="name" /> </property> <!-- 对于部门来说,要存储多个员工,需采用集合表示 ,配置和普通集合差不多 inerse:属性 默认为false:表示本方维护关联关系 true:表示本方放弃维护 只是影响是否能设置外键列的值(设成有效值或是null值),对获取信息没有影响。 class:表示关联的实体类型,如果不在同一个包下,请使用全限定名 cascade属性: 默认为none,代表不级联。 级联是指操作主对象时,对关联的对象也做相同的操作。 可设为:delete, save-update, all, none ... 设置为delete表示删除部门时,部门下的所有员工也会删除 --> <set name="employees" inverse="true" cascade="delete"> <key> <column name="departID" /> </key> <!-- 多对一:,部门员工,对于部门来说一对多,所以使用 one-to-many标签,直接写要对应的类即可 ,因为hibernate 可以通过映射找到类,进而找到表的名称,不写也可以--> <one-to-many class="Employee" /> </set> </class> </hibernate-mapping>员工类(Employee)和部门表的(Employee.hbm.xml)xml配置文件:
package hibernate_oneToMany; public class Employee { private Integer id; private String name; private Department department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + "]"; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2016-4-2 13:39:24 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping package="hibernate_oneToMany"> <class name="Employee" table="employees"> <id name="id" type="int"> <column name="id" /> <generator class="native" /> </id> <property name="name" type="string"> <column name="name" /> </property> <!-- 一对多: 对于员工来说:多对一,所以使用many-to-one 指定要对应的类和数据库数据库中药显示的列的名称, 该名称的内容是被引用的外键值,在这里就是部门ID 注意many-to-one引用的是集合的Department的主键,当Department中集合外键是Department中的主键时,注意二者要为一致 --> <many-to-one name="department" class="Department" > <column name="departID" /> </many-to-one> </class> </hibernate-mapping>测试操作类:
package hibernate_oneToMany; import hibernate_manyeToMany.Student; import hibernate_manyeToMany.Teacher; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.junit.Test; /** * @author 梁磊 * */ public class App { static SessionFactory sessionFactory; static{ //初始化数据 sessionFactory=new Configuration().configure() .addClass(Department.class).addClass(Employee.class) .buildSessionFactory(); } @Test public void testSave() { Session session=sessionFactory.openSession(); session.beginTransaction(); //实例对象 Employee employee1=new Employee(); Employee employee2=new Employee(); employee1.setName("leige"); employee2.setName("leige2"); Department department=new Department(); department.setName("开发部"); //关联起来 department.getEmployees().add(employee2); department.getEmployees().add(employee1); employee1.setDepartment(department); employee2.setDepartment(department); //保存时建议报没有外键的一方放在前面,这与数据库外键有关,不会生成update语句 session.save(department); session.save(employee1); session.save(employee2); session.getTransaction().commit(); } @Test public void testGet() { //测试获取一方,获取查出另一方 //查询部门,获取员工 /*Session session=sessionFactory.openSession(); session.beginTransaction(); Department department=(Department) session.get(Department.class, 1); System.out.println(department); System.out.println(department.getEmployees()); session.getTransaction().commit();*/ //获取员工查询,部门 Session session=sessionFactory.openSession(); session.beginTransaction(); Employee employee=(Employee) session.get(Employee.class, 1); session.getTransaction().commit(); System.out.println(employee); System.out.println(employee.getDepartment()); } /** * 测试解除关联关系 */ @Test public void testRemove() { //解除关系时先get在解除 Session session=sessionFactory.openSession(); session.beginTransaction(); //解除部门下的所有员工,这个操作与inverse属性有关,由于Department不维护关系,所以无法从部门方解除所有员工 /* * Department department=(Department) session.get(Department.class, 1); * department.getEmployees().clear(); session.getTransaction().commit();*/ //但是要想解除所有员工,只能从维护关系的一方解除,这里是解除,不是删除,(解除是解除关联关系,删除是删除数据) //所以操作为 List<Employee> employees=session.createQuery("from Employee where departID=1").list(); for(Employee em:employees) em.setDepartment(null); session.getTransaction().commit(); } /** * 测试删除一方 */ @Test public void testDelete() { //删除对象首先获取对象 Session session=sessionFactory.openSession(); session.beginTransaction(); //删除部门,这个操作与inverse属性有关,本例由于Department不维护关系,所以无法删除部门有员工的部门,因为有外键的引用 //所以只能删除空部门, /* Department department=(Department) session.get(Department.class, 1); session.delete(department); session.getTransaction().commit();*/ //删除员工,删除有外键的一方和外键方没有关系 Employee employee=(Employee) session.get(Employee.class, 1); session.delete(employee); session.getTransaction().commit(); } }
各位初学者,可以试着测试,可以得到想要的,结果,一定要学会看异常信息
二:多对多映射本例采用学生老师集合映射:
Teacher和Teacher.hbm.xml文件如下:
Teacher:
package hibernate_manyeToMany; import java.util.HashSet; import java.util.Set; public class Teacher { private int id; private String name; private Set<Student> students=new HashSet<Student>(); 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; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } @Override public String toString() { return "Teacher [id=" + id + ", name=" + name + "]"; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2016-4-2 17:09:56 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping package="hibernate_manyeToMany"> <class name="Teacher" table="teacher"> <id name="id" type="int"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="string"> <column name="name" /> </property> <!-- 多对多关系,处于对hibernate自动生成sql语句优化的考虑,建议设置一方维护, 例如本例中应该设置学生维护关系,比较符合实际, 所以Teacher中设置inverse为true,表示本方放弃维护关系 student中inverse为false,表示本方维护关系 ,--> <set name="students" table="teacher_student" inverse="true" lazy="true"> <key> <column name="teacherID" /> </key> <many-to-many class="Student" column="studentID"></many-to-many> </set> </class> </hibernate-mapping>
Student:
package hibernate_manyeToMany; import java.util.HashSet; import java.util.Set; public class Student { private int id; private String name; private Set<Teacher> teachaers=new HashSet<Teacher>(); 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; } public Set<Teacher> getTeachaers() { return teachaers; } public void setTeachaers(Set<Teacher> teachaers) { this.teachaers = teachaers; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2016-4-2 17:09:56 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping package="hibernate_manyeToMany"> <class name="Student" table="student"> <id name="id" type="int"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="string"> <column name="name" /> </property> <!-- 多对多关系,处于对hibernate自动生成sql语句优化的考虑,建议设置一方维护, 例如本例中应该设置学生维护关系,比较符合实际, 所以Teacher中设置inverse为true,表示本方放弃维护关系 student中inverse为false,表示本方维护关系, 另外:无序集合是可以排序的sort属性sort="unsorted|natural|comparatorClass" :指定排序,这是在内存中排序,效率较低,一般不使用 order-by属性:写的是order by 子句,是SQL语句,是操作的集合表。 这是在查询数据时指定orderby子句,效率高,经常使用。 lazy建议为false对集合属性设置懒加载,这样可以减少查询数据的次数,只有在需要集合元素时才会真正查询数据库 --> <set name="teachaers" table="teacher_student" inverse="false" lazy="false" sort="unsorted" order-by="name desc"> <key> <column name="studentID" /> </key> <many-to-many class="Teacher" column="teacherID"></many-to-many> </set> </class> </hibernate-mapping>
package hibernate_manyeToMany; import hibernate_collection.User; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.junit.Test; /** * @author 梁磊 * */ public class App { static SessionFactory sessionFactory; static{ //初始化数据 sessionFactory=new Configuration().configure() .addClass(Student.class).addClass(Teacher.class) .buildSessionFactory(); } /** * 测试保存 */ @Test public void testSave() { Teacher teacher1=new Teacher(); //新建对象 Teacher teacher2=new Teacher(); teacher1.setName("找老师"); teacher2.setName("梁老师"); Student student1=new Student(); Student student2=new Student(); student1.setName("student1"); student2.setName("学生2"); //关联起来 //老师关联学生 teacher1.getStudents().add(student1); teacher1.getStudents().add(student2); teacher2.getStudents().add(student2); teacher2.getStudents().add(student1); //学生关联老师 student1.getTeachaers().add(teacher1); student1.getTeachaers().add(teacher2); student2.getTeachaers().add(teacher2); student2.getTeachaers().add(teacher1); //保存对象 Session session=sessionFactory.openSession(); session.beginTransaction(); session.save(teacher2); session.save(teacher1); session.save(student1); session.save(student2); session.getTransaction().commit(); } /** * 测试获取 */ @Test public void testGet() { // Session session=sessionFactory.openSession(); session.beginTransaction(); //获取是与inverse属性无关,不管是否维护关系都能一方获取另一方的数据 //获取老师显示学生 Teacher teacher=(Teacher) session.get(Teacher.class, 1); System.out.println(teacher); System.out.println(teacher.getStudents()); //获取学生显示老师 Student student=(Student) session.get(Student.class, 1); System.out.println(student); System.out.println(student.getTeachaers()); session.getTransaction().commit(); } /** * 测试解除关联关系 */ @Test public void testRemove() { Session session=sessionFactory.openSession(); session.beginTransaction(); //测试解除学生关系,不会产生效果,不会执行sql语句,因为,Teacher对象不维护关系,即inverse属性为true Teacher teacher=(Teacher) session.get(Teacher.class, 1); teacher.getStudents().clear(); //测试学生和老师解除关系 /* Student student=(Student) session.get(Student.class, 1); student.getTeachaers().clear();*/ session.getTransaction().commit(); } /** * 测试删除一方 */ @Test public void testDelete() { Session session=sessionFactory.openSession(); session.beginTransaction(); //测试删除老师,会报错,因为老师方设置inverse属性为true,不维护关系,所以会执行sql语句,但是因为有外键引用所以会产生sql异常 /*Teacher teacher=(Teacher) session.get(Teacher.class, 1); session.delete(teacher);*/ //测试删除学生,会成功,因为学生维护关系,会删除学生表中记录,然后删除中间表teacher_student中记录 Student student=(Student) session.get(Student.class, 1); session.delete(student); session.getTransaction().commit(); } }