在hibernate中,通常配置对象关系映射关系有两种,一种是基于xml的方式,另一种是基于annotation的注解方式,熟话说,萝卜青菜,可有所爱,每个人都有自己喜欢的配置方式,我在试了这两种方式以后,发现使用annotation的方式可以更简介,所以这里就简单记录下通过annotation来配置各种映射关系,在hibernate4以后已经将annotation的jar包集成进来了,如果使用hibernate3的版本就需要引入annotation的jar包。
一、单对象操作
@Entity ---> 如果我们当前这个bean要设置成实体对象,就需要加上Entity这个注解 @Table(name="t_user") ----> 设置数据库的表名 public class User { private int id; private String username; private String password; private Date born; private Date registerDate; @Column(name="register_date") ---> Column中的name属性对应了数据库的该字段名字,里面还有其他属性,例如length,nullable等等 public Date getRegisterDate() { return registerDate; } public void setRegisterDate(Date registerDate) { this.registerDate = registerDate; } @Id ---> 定义为数据库的主键ID (建议不要在属性上引入注解,因为属性是private的,如果引入注解会破坏其封装特性,所以建议在getter方法上加入注解) @GeneratedValue ----> ID的生成策略为自动生成 public int getId() { return id; } public void setId(int id) { this.id = id; } ............ }
最后只需要在hibernate.cfg.xml文件里面将该实体类加进去即可:
<!-- 基于annotation的配置 --> <mapping class="com.xiaoluo.bean.User"/> <!-- 基于hbm.xml配置文件 --> <mapping resource="com/xiaoluo/bean/User.hbm.xml"/>
这样我们就可以写测试类来进行我们的CRUD操作了。
二、一对多的映射(one-to-many)
这里我们定义了两个实体类,一个是ClassRoom,一个是Student,这两者是一对多的关联关系。
ClassRoom类:
@Entity @Table(name="t_classroom") public class ClassRoom { private int id; private String className; private Set<Student> students; public ClassRoom() { students = new HashSet<Student>(); } public void addStudent(Student student) { students.add(student); } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } @OneToMany(mappedBy="room") ---> OneToMany指定了一对多的关系,mappedBy="room"指定了由多的那一方来维护关联关系,mappedBy指的是多的一方对1的这一方的依赖的属性,(注意:如果没有指定由谁来维护关联关系,则系统会给我们创建一张中间表) @LazyCollection(LazyCollectionOption.EXTRA) ---> LazyCollection属性设置成EXTRA指定了当如果查询数据的个数时候,只会发出一条 count(*)的语句,提高性能 public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } }
Student类:
@Entity @Table(name="t_student") public class Student { private int id; private String name; private int age; private ClassRoom room; @ManyToOne(fetch=FetchType.LAZY) ---> ManyToOne指定了多对一的关系,fetch=FetchType.LAZY属性表示在多的那一方通过延迟加载的方式加载对象(默认不是延迟加载) @JoinColumn(name="rid") ---> 通过 JoinColumn 的name属性指定了外键的名称 rid (注意:如果我们不通过JoinColum来指定外键的名称,系统会给我们声明一个名称) public ClassRoom getRoom() { return room; } public void setRoom(ClassRoom room) { this.room = room; } @Id @GeneratedValue 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 int getAge() { return age; } public void setAge(int age) { this.age = age; } }
三、一对一映射(One-to-One)
一对一关系这里定义了一个Person对象以及一个IDCard对象
Person类:
@Entity @Table(name="t_person") public class Person { private int id; private String name; private IDCard card; @OneToOne(mappedBy="person") ---> 指定了OneToOne的关联关系,mappedBy同样指定由对方来进行维护关联关系 public IDCard getCard() { return card; } public void setCard(IDCard card) { this.card = card; } @Id @GeneratedValue 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; } }
IDCard类:
@Entity @Table(name="t_id_card") public class IDCard { private int id; private String no; private Person person; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getNo() { return no; } public void setNo(String no) { this.no = no; } @OneToOne ---> OnetoOne指定了一对一的关联关系,一对一中随便指定一方来维护映射关系,这里选择IDCard来进行维护 @JoinColumn(name="pid") ---> 指定外键的名字 pid public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
注意:在判断到底是谁维护关联关系时,可以通过查看外键,哪个实体类定义了外键,哪个类就负责维护关联关系。
四、Many-to-Many映射(多对多映射关系)
多对多这里通常有两种处理方式,一种是通过建立一张中间表,然后由任一一个多的一方来维护关联关系,另一种就是将多对多拆分成两个一对多的关联关系
1.通过中间表由任一一个多的一方来维护关联关系
Teacher类:
@Entity @Table(name="t_teacher") public class Teacher { private int id; private String name; private Set<Course> courses; public Teacher() { courses = new HashSet<Course>(); } public void addCourse(Course course) { courses.add(course); } @Id @GeneratedValue 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; } @ManyToMany(mappedBy="teachers") ---> 表示由Course那一方来进行维护 public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } }
Course类:
@Entity @Table(name="t_course") public class Course { private int id; private String name; private Set<Teacher> teachers; public Course() { teachers = new HashSet<Teacher>(); } public void addTeacher(Teacher teacher) { teachers.add(teacher); } @ManyToMany ---> ManyToMany指定多对多的关联关系 @JoinTable(name="t_teacher_course", joinColumns={ @JoinColumn(name="cid")}, inverseJoinColumns={ @JoinColumn(name = "tid") }) ---> 因为多对多之间会通过一张中间表来维护两表直接的关系,所以通过 JoinTable 这个注解来声明,name就是指定了中间表的名字,JoinColumns是一个 @JoinColumn类型的数组,表示的是我这方在对方中的外键名称,我方是Course,所以在对方外键的名称就是 rid,inverseJoinColumns也是一个 @JoinColumn类型的数组,表示的是对方在我这放中的外键名称,对方是Teacher,所以在我方外键的名称就是 tid public Set<Teacher> getTeachers() { return teachers; } public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; } @Id @GeneratedValue 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; } }
2.将Many-to-Many拆分成两个One-to-Many的映射(Admin、Role、AdminRole)
Admin类:
@Entity @Table(name="t_admin") public class Admin { private int id; private String name; private Set<AdminRole> ars; public Admin() { ars = new HashSet<AdminRole>(); } public void add(AdminRole ar) { ars.add(ar); } @Id @GeneratedValue 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; } @OneToMany(mappedBy="admin") ---> OneToMany关联到了AdminRole这个类,由AdminRole这个类来维护多对一的关系,mappedBy="admin" @LazyCollection(LazyCollectionOption.EXTRA) public Set<AdminRole> getArs() { return ars; } public void setArs(Set<AdminRole> ars) { this.ars = ars; } }
Role类:
@Entity @Table(name="t_role") public class Role { private int id; private String name; private Set<AdminRole> ars; public Role() { ars = new HashSet<AdminRole>(); } public void add(AdminRole ar) { ars.add(ar); } @Id @GeneratedValue 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; } @OneToMany(mappedBy="role") ---> OneToMany指定了由AdminRole这个类来维护多对一的关联关系,mappedBy="role" @LazyCollection(LazyCollectionOption.EXTRA) public Set<AdminRole> getArs() { return ars; } public void setArs(Set<AdminRole> ars) { this.ars = ars; } }
AdminRole类:
@Entity @Table(name="t_admin_role") public class AdminRole { private int id; private String name; private Admin admin; private Role role; @Id @GeneratedValue 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; } @ManyToOne ---> ManyToOne关联到Admin @JoinColumn(name="aid") public Admin getAdmin() { return admin; } public void setAdmin(Admin admin) { this.admin = admin; } @ManyToOne ---> @JoinColumn(name="rid") public Role getRole() { return role; } public void setRole(Role role) { this.role = role; } }
小技巧:通过hibernate来进行插入操作的时候,不管是一对多、一对一还是多对多,都只需要记住一点,在哪个实体类声明了外键,就由哪个类来维护关系,在保存数据时,总是先保存的是没有维护关联关系的那一方的数据,后保存维护了关联关系的那一方的数据,如:
Person p = new Person(); p.setName("xiaoluo"); session.save(p); IDCard card = new IDCard(); card.setNo("1111111111"); card.setPerson(p); session.save(card);
Hibernate的Session机制
hibernate的缓存机制对应于两种缓存:session中的一级缓存,sessionFactoury中的二级缓存。
缓存中的对象存在内存,如果数据量大大得时候,可以存在硬盘。
1.session缓存实现原理
实现原理:Session缓存是由它的实现类SessionImpl中定义的一些集合属性构成的,原理是保证有一个引用在关联着某个持久化对象,保持它的生命周期不会结束。这样说来,
当我们User user=new User(),然后对user数值值赋值,使它对应数据库一条记录,但是由于其没有与session相关联,即session中没有引用关联这个对象,所以它是托管状态的。
2,使用flush清理缓存
1)使用flush()可以清理掉缓存。但是一般不显示调用,显示调用会浪费系统资源(与数据库交互),一般由hibernate自己维护,尽量延迟到最后调用它。
2)session.save(user);
session.save(student)
session.save(employee)
System.out.println("=======");
transaction.commit();
一般情况下事务提交的时候,hibernate才会调用flush()方法清理掉缓存。控制台显示sql语句:
Hibernate: insert into user(name, dept_id, id) values(?, ?, ?)
=======
Hibernate: insert into student(name, dept_id, skill, id) values(?, ?, ?, ?)
Hibernate: insert into employee(name, dept_id, sell, id) values(?, ?, ?, ?)
原因解释:
先打印=======,说明在提交的时候,hibernate才清理缓存,将缓存中的数据与数据库中同步。但是为什么对user的插入出现在======之前呢?
这是因为user的主键是自增长的,user必须保存之后才能拿到主键。
3,缓存的作用
1)更新对象时,减少与数据库打交道的次数
从上面的例子可以知道,缓存的存在,可以使对象的改变存在缓存中,当使用flush清理缓存时,可以对对象的所有改变进行批量操作(与数据库打一次交道),减少与数据库打交道的次数。
2)查询对象时,减少与数据库打交道的次数
Customer c1 = (Customer)session.get(Customer.class, 1L);
Customer c2 = (Customer)session.get(Customer.class, 1L);
hibernate比较聪明,由于缓存中有此对象了,所以第二次get()时不会从数据库中查询,而是直接使用缓存中的对象。(注意缓存要定时清理,与数据库同步)
Hibernate实体关联
<mapping resource="com/neusoft/entity/Address.hbm.xml"/>
注意级联(<many-to-one cascade="true">)
一对多(多对一):a)多对一单向关联
annotation:在多方的实体类中添加@ManyToOne
如果需要修改关联字段的名字,添加@JoinColumn(name="Class_ID")
xml:
在多方xml文件中添加
<hibernate-mapping package="com.neusoft.entity">
<class name="Person">
<id name="id">
<generator class="native"></generator>
<!--自动生成id -->
</id>
<property name="name"></property>
<property name="age"></property>
<many-to-one name="address" class="Address"></many-to-one>
<!--many-to-one 和propertity层次相同-->
</class>
</hibernate-mapping>
b)一对多单向关联(会生成中间表)
annotation:在一方中实体类中加入 private Set<Student> stus=new HashSet<Student>();
在一方的实体类中添加@OneToMany
XML:
在一方添加
<hibernate-mapping package="com.neusoft.hibernate">
<class name="Class" >
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
<set name="stus">
<key column="c_id"></key>
<one-to-many class="Student"/>
</set>
<!-- set和propertity层次相同-->
</class>
</hibernate-mapping>
c)一对多(多对一)双向关联
annotation:在多方添加@ManyToOne,
在一方添加@OneToMany(mappedBy="c")由另一个many的c(c为many中一个属性)维护关系
xml:在一方添加:
<set name="stus">
<key column="c_id"></key>
<one-to-many class="Student"/>
</set>
在多方添加:
<many-to-one name="c" class="Class" column="c_id"></many-to-one>
d)多对多:
1单向关联
annotation:在某一方添加@ManyToMany
如果需要修改中间表的表名、列名
@JoinTable(name="s_c", joinColumns={@JoinColumn(name="s_id")},
inverseJoinColumns={@JoinColumn(name="c_id")})
xml:
<set name="courses" table="stu_course">
<key column="stu_id"></key>
<many-to-many class="Course" column="CID"></many-to-many>
</set>
2双向关联:
annotation:双方均添加@ManyToMany,在其中一方上添加 (mappedBy="courses")
由另一个many的courses(courses为many中一个属性)维护关系
xml: inverse="true"关系由另一方维护默认为false关系由双方维护
<set name="courses" inverse="true" >
<key column="stu_id"></key>
<many-to-many class="Course"></many-to-many>
</set>
<set name="stus" table="stu_course" inverse="false">
<key column="CID"></key>
<many-to-many class="Student" column="stu_id"></many-to-many>
</set>
有关联关系的CRUD的级联:
添加:cascade={CascadeType.ALL}
查询:fetch
一对多默认值是LAZY(懒执行,如果用不到数据不执行查询)
多对一默认值是EAGER(渴望默认查询所有)
千万不要双方均设EAGER
修改:
删除:双方均做了级联,级联删除,数据有可能都删掉
解决方法:
1)取消关联关系:将对应属性 setXxx(null);
2)HQL:
Query q=session.createQuery("delete from Student s where id=211");
q.executeUpdate();
关于Java的垃圾回收
集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。