加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

第三章:域模型和元数据--POJO的getter和setter方法

(2012-04-19 20:59:56)
标签:

hibernate

it

分类: Hibernate和数据库

POJOgettersetter方法

本文源于Hibernate in action

大关总结

1. 属性访问的类型和特点(4.4.1

持久化引擎可以直接或者通过访问器(即getset方法)访问属性。Hibernate中,使用XML配置形式,通过default-access设置默认的访问形式,可以有四种选项,default-access=”field|property|noop|custom.Class”。而使用Annotations形式的配置方式,默认的访问形式由@Id标记放置的位置决定,当@Id标记在属性上声明,而不是在getter方法上,则所有的其它属性默认采用属性访问形式(即不通过gettersetter方法,直接访问属性)。这种方式是JTA默认的。

当然,Hibernate提供了一种更灵活的方式,使用@org.hibernate.annotations.AccessType可以更改这种默认配置,策略如下:

1)如果AccessType在类(实体)级别设置,类中的所有属性都使用这个默认访问方式,除非某个属性在其属性或者getter方法上重写了访问方式,重写访问方式将覆盖默认方式。但是类级别的设置将会重写@Id方式的默认访问方式。

2)如果实体默认设置或者显示设置了属性访问,在属性上设置AccessType(“property”),更改访问方式为getter/setter

3)如果实体默认设置为property访问,而在getter方法上标记了AccessType(“field”),则getter方法在运行过程中将转换成field访问的形式。

4@Embedded将会继承拥有者类的默认访问方式。

5@MappedSuperclass与映射的实体有关。

2. 属性访问方法(gettersetter)(3.2.3

你可以选择使用直接访问属性或者使用settergetter方法的形式修改类属性。对于类的设计并不会受到Hibernate的具体访问形式的干扰。可以选择将类中部分属性的settergetter方法设置为私有(例如,Idsetter方法一般为私有的,因为Id属性是通过系统自动生成而不是人为设定,还有其它系统设定属性可以设置为私有访问方法甚至将相应的gettersetter方法完全省略)。但是有些时候,gettersetter方法可能具有某些特殊意义。

2.1 在访问方法中添加用户逻辑

有时用户期望在访问方法中添加一些逻辑,如下例所示,希望在数据库中将用户名字分成firstNamelastName分别存储,而在用户获取的时候,name是一个属性,如下所示:

import org.hibernate.annotations.AccessType;

@Entity

@Table(name="USERS")

public class User {

 

    @Id

    @GeneratedValue

    @Column(name="USER_ID")

    private Long id;

   

    @Column(name="USER_FIRST_NAME")

    private String firstName;

   

    @Column(name="USER_LAST_NAME")

    private String lastName;

   

    public Long getId()

    {

       return id;

    }

   

    public String getUserName()

    {

       return firstName+" "+lastName;

    }

   

    public void setUserName(String name)

    {

       StringTokenizer t = new StringTokenizer(name);

       firstName = t.nextToken();

       lastName = t.nextToken();

    }

   

}

测试代码如下:

       UserDAO ud = context.getBean(UserDAO.class);

       User user = new User();

       user.setUserName("Stiffer Ruffer");

       ud.saveUser(user);

      

       List<User> lu = ud.getAllUsers();

       for(User u : lu)

       {

           System.out.println(u.getUserName());

       }

输出信息是:

Hibernate:

    insert

    into

        USERS

        (USER_FIRST_NAME, USER_LAST_NAME)

    values

        (?, ?)

Hibernate:

    select

        user0_.USER_ID as USER1_1_,

        user0_.USER_FIRST_NAME as USER2_1_,

        user0_.USER_LAST_NAME as USER3_1_

    from

        USERS user0_

Stiffer Ruffer

数据库中的表内容是:

mysql> select * from users;

+---------+-----------------+----------------+

| USER_ID | USER_FIRST_NAME | USER_LAST_NAME |

+---------+-----------------+----------------+

|       1 | Stiffer         | Ruffer         |

+---------+-----------------+----------------+

1 row in set (0.02 sec)

在这个例子中,@Id标记在属性上,因此,Hibernate默认使用field形式直接访问属性(而不是gettersetter)。firstNamelastName被设置成私有的,并且没有提供gettersetter方法,但是Hibernate能够直接通过访问firstNamelastName属性,读取其值存储于数据库中,或者从数据库中读取值直接赋值给这两个属性。使用setUserName属性为firstNamelastName添加了用户自定义的逻辑。

在某些时候,可以在setter方法中添加数据验证逻辑,当setter的参数没有通过验证时,需要抛出异常,系统事务管理接收到异常会将事务自动回滚,并提示用户,用户提供的数据无法通过验证,产生异常。

2.2 多对一关系处理

现在考虑多对一关系,在多对一关系中,如果期望对象能够双向导航,需要创建对应的两个属性,我们以StudentTeacher模型为例,现在假设每个学生只能由一个老师管理,而每个老师可以管理多个学生,因此,这即形成了一个多对一的关系。此时,Student类中需要存放一个Teacher属性,而Teacher类中需要设置一个Student集合。解决这种双向导航的类问题远比想象中的复杂,而很多程序开发者并没有发现其内在的复杂性。下面以TeacherStudent建立关联关系为例,代码如下:

       Student s = new Student();

       Teacher t = new Teacher();

       s.setTeacher(t);

       t.getStudents().add(s);

从实际逻辑上讲,在对Student设置了teacher属性时,teacher应当知道有一个新同学应当加入它的students集合中,因此第四句话似乎显得多余。从数据库角度看,关联关系只不过是一个外键的建立,就是STUDENT表中存放一个TEACHER_ID的外键(外键要放到多的一方),数据库中的关系永远是单向的,而对于多对一关系的双向导航类来说,需要将数据库中的一个单向关系映射成一个双向关系,此时,按照传统的gettersetter方法将无法满足需求。因此使用下面的代码和策略:

@Entity

@Table(name="STUDENT")

public class Student {

    @Id

    @GeneratedValue

    @Column(name="STUDENT_ID")

    private Long id;

   

    @Column(name="STUDENT_NAME")

    private String name;

   

    @ManyToOne(cascade=CascadeType.ALL)

    @JoinColumn(name ="TEACHER_ID")

    private Teacher teacher;

   

   

    public Long getId() {

       return id;

    }

   

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

 

    public void setTeacher(Teacher teacher) {

        if(this.teacher != null)

           this.teacher.removeStudent(this);

      

       this.teacher = teacher;

      

       if(teacher != null)

           teacher.addStudent(this);  

}

 

    public Teacher getTeacher() {

       return teacher;

    }

   

}

@Entity

@Table(name="TEACHER")

public class Teacher {

 

    @Id

    @GeneratedValue

    @Column(name="TEACHER_ID")

    private Long id;

   

    @Column(name="TEACHER_NAME")

    private String name;

   

    @OneToMany(mappedBy="teacher",cascade=CascadeType.ALL)

    private Set<Student> students;

   

   

    public Long getId() {

       return id;

    }

   

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

 

    public Set<Student> getStudents() {

       if(students == null)

           students = new HashSet<Student>();

       return Collections.unmodifiableSet(students);

    }

 

    public void removeStudent(Student student) {

       if(students != null)

           students.remove(student);      

    }

 

    public void addStudent(Student student) {

       if(students == null)

           students = new HashSet<Student>();

       students.add(student);     

    }

}

在为Student添加Teacher时,需要将student和其原有teacher间的关系撤销,之后将当前teacher更新新值,并将自己添加到新teacherstudent集合中。另一方面,Teacher需要提供添加单个学生的方法addStudent和移除student的方法removeStudent,需要注意的是,Teacher类没有提供setStudents方法,因为,我们不期望提供对students集合的直接更改,students集合的更改应当是通过Student类的addTeacher方法间接完成的。(需要注意的是addStudent方法和removeStudent方法,只是为Student类提供操作students集合的方法,对于外部逻辑应该不能直接使用这些方法,我们找不到一个更好的可见性关键字形容它,因此使用了public(感觉C++中的friend关键字,似乎满足需求,但java没有))。在Teacher类中,对获取students集合的方法进行了限制,可以使用Collections.unmodifiableCollection(c)或者Collection.unmodifiableSet(c)两个方法来限制返回的集合不会被外部修改。因此,如果想修改StudentTeacher间的关联关系,只能通过StudentsetTeacher方法,但是,如果想访问他们之间的关系,则可以通过使用Student中的getTeacher方法或者使用Teacher中的getStudents方法,因此,针对修改是单向的,而针对访问是双向的。现在当需要建立StudentTeacher间的关系时,只需要下面的语句。

       Student s = new Student();

       Teacher t = new Teacher();

       s.setTeacher(t);

双向导航关系的代码被称为脚架式代码(scaffolding code)。在数据库角度来看,因为外键是建立在Student一侧,因此,只有StudentTeacher属性被设置了,当前的student才具有一个外键,如果仅TeacherStudent集合中包含了student对象,而student对象并没有设置Teacher属性(即设置是单向的),在数据库中该student条目将不会产生对应与teacher的外键。如下代码所示:

       Student s = new Student();

       Teacher t = new Teacher();

       t.addStudent(s);

此时数据库中不会建立studentteacher的关系。在数据库中t的外键是NULL

多对多关系与上述处理方式类似,这里不再累述。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有