《企业级开发ORM对象映射》由会员分享,可在线阅读,更多相关《企业级开发ORM对象映射(50页珍藏版)》请在金锄头文库上搜索。
1、第三章第三章实体实体Bean与与ORM关系对象映射关系对象映射 课程内容课程内容实体与实体Bean的概念实体Bean的开发JBoss数据源的配置单表映射的实体Bean的开发对象/关系映射EntityManager API以及实体Bean的生命周期一对一、一对多、多对一、多对多组合主键的概念MDB组件及开发概述概述 提供标准的提供标准的O/R Mapping。 JPA没有同没有同Java EE容器绑定在一起。容器绑定在一起。 定义了服务提供者接口(定义了服务提供者接口(SPI)。)。实体不同于实体Bean,它们是不同的对象。而且,实体也不是实体Bean的后续产物,它是一种全新的编程概念,在Jav
2、a持久化API(Java Persistence API,JPA)规范中定义了以下内容:实体实体实体同实体同Session Bean的差异如下的差异如下 : 实体存在客户可见的、持久化身份(主键)实体存在客户可见的、持久化身份(主键) 实体存在持久化、客户可见的状态实体存在持久化、客户可见的状态 不能够直接通过远程访问到实体不能够直接通过远程访问到实体 实体的生命周期可能与应用本身的生命周期无关实体的生命周期可能与应用本身的生命周期无关实体:在JPA规范中持久化数据对象就是实体。通过持久化机制能够将Java对象存储到持久化源中,这类对象表示数据,无论是简单的或者是复杂的,持久化数据对象都能够表
3、示。 实体类实体类实体中常用到的注释:实体中常用到的注释: Entity注释:将类标识为注释:将类标识为JPA实体。实体。 Table注释:指定实体的主表注释:指定实体的主表 Id注释:实体必须声明主键注释:实体必须声明主键 GeneratedValue注释:指定主键的生成策略。注释:指定主键的生成策略。 Column注释:指定持久化属性或者是成员变量映射到的列。注释:指定持久化属性或者是成员变量映射到的列。 Temporal注释:指定将成员属性和成员变量持久化为时间类型。注释:指定将成员属性和成员变量持久化为时间类型。 实体类:实体类类似于其它EJB组件,它们也是POJO类,存在元数据注释,
4、可以使用XML部署符定义它们。 实体实体BEAN的组成文件的组成文件 java:/DefaultMySqlDS 一个实体Bean由实体类和persistence.xml配置文件组成,该文件要创建在Ejb-jar文件的META-INF目录下,persistence.xml指定实体Bean使用的数据源和及EntityManager对象的默认行为。persistence.xml文件的配置说明如下所示:实体实体BEAN的开发的开发 在开发实体在开发实体Bean的时候主要的工作是对实体类的代码编写的时候主要的工作是对实体类的代码编写工作。让我们看一个工作。让我们看一个Customer实体的简单例子:实体
5、的简单例子: import javax.persistence.*;Entitypublic class Customer private int id;private String name;IdGeneratedValuepublic int getId() return id;public void setId(int id) this.id = id;String getName() return name;public void setName(String name) this.name = name;JBoss数据源的配置数据源的配置 注意:数据源文件配置好后需要放置在注意:数据
6、源文件配置好后需要放置在JBoss安装目录安装目录“/server/config-name/deploy”目录下,我们之前安装目录下,我们之前安装JBoss时采用时采用config-name为:为:“default”,所以路径为:,所以路径为:JBoss安装安装目录目录“/server/default /deploy”目录。目录。 JBoss有一个默认的数据源DefaultDS,它使用JBoss内置的HSQLDB数据库。实际应用中我们可能使用不同的数据库,如常用的MySql、Ms SQL Server、Oracle等等。各种数据库的数据源配置模板我们可以在JBoss安装目录“/docs/exa
7、mples/jca”目录下找到,可以发现配置文件的默认名称规则是:“数据库名+-ds.xml”。 EntityManager API与实体与实体BEAN的生命周期的生命周期 EntityManager的获取可以通过的获取可以通过PersistenceContext 注释由注释由EJB容器动态注入,例如:容器动态注入,例如: PersistenceContext(unitName=”MyDatabase”) EntutyManager em;EntityManager API : EntityManager是应用访问持久化上下文中的实体的接口,顾名思义,EntityManager是管理所有EJB
8、3.0运行环境中的所有的Entity。EntityManager根据运行的环境不同分为容器管理的EntityManager和应用管理的EntityManager。 EntityManager常用的常用的API 实体获取实体获取find() 实体添加实体添加persist() 实体更新实体更新merge() 实体删除实体删除remove() 执行执行EJB3 QL操作操作createQuery()刷新刷新flush() 实体实体BEAN的生命周期的生命周期新建(新建(new),此时,在内存中已经创建了实体),此时,在内存中已经创建了实体实例。实例。受管(受管(managed),此时,实体已经在数
9、据库中),此时,实体已经在数据库中存在了持久化身份存在了持久化身份 。分离(分离(detached),此时,实体具有持久化身份,),此时,实体具有持久化身份,但它不再同持久化上下文关联了但它不再同持久化上下文关联了 。删除(删除(removed)此时,实体同持久化上下文进)此时,实体同持久化上下文进行了关联,但是客户已经打算从数据库中销毁这一行了关联,但是客户已经打算从数据库中销毁这一实体了。实体了。 单表映射的实体单表映射的实体BEAN 表表3-1需要映射的数据库表需要映射的数据库表字段名称字段类型属性描述personid(主键主键)Int(11) not null人员人员IDpersonN
10、ame Varchar(32)not null姓名姓名sexTinyint(1)not null性别性别ageSmallint(6)not null年龄年龄birthdayDatetime null出生日期出生日期对象对象/关系映射关系映射 下面给出了各种常见的关系类型:下面给出了各种常见的关系类型: 1:1关系关系,此时,一条记录仅仅会同一条记录进行关联。比如人和身份证号这这种关系。 1:N关系关系,一条记录会同许多其它的记录进行关联。比如经理和其职员构成了这种关系。M:1关系关系,此时,多条记录会同一条记录进行关联。比如银行账号和人构成了这种关系。 M:N关系关系,多条记录同许多其它的记录
11、进行关联。比如作家和杂志社就构成了这种关系。一对一映射一对一映射关系实例关系实例 :Person:IdcardCar:WindshieldOrder:Shipment一个一个Demo:以:以Person和和Idcard为例为例 定义一个定义一个Session Bean作为它的使用者。下面是作为它的使用者。下面是Session Bean的业务接口,它定义了四个业务方法,的业务接口,它定义了四个业务方法,其业务功能分别是:其业务功能分别是: insertPerson():添加一个人员到数据库。:添加一个人员到数据库。 getPersonById():获取指定编号的人员。:获取指定编号的人员。 up
12、datePersonInfor():更新人员的信息。:更新人员的信息。 deletePerson():删除人员,连同身份证一同删除。:删除人员,连同身份证一同删除。字段名称字段类型属性描述Id(主键主键)Int(11) not null流水号流水号cardNumVarchar(18)not null身份证号身份证号personidInt(11) not null外键指向外键指向person表的表的personid字段名称字段类型属性描述personid(主键主键)Int(11) not null人员人员IDpersonName Varchar(32)not null姓名姓名sexTinyint
13、(1)not null性别性别ageSmallint(6)not null年龄年龄birthdayDatetime null出生日期出生日期表3-2 Person表: 表3-3 Idcard表:OneToOneOneToOne注释:用途:定义定义1:1关系中的另一单值对象。通常是从被引用的就可关系中的另一单值对象。通常是从被引用的就可以判断出目标实体,因此开发者不需要显式的指定目标实体。以判断出目标实体,因此开发者不需要显式的指定目标实体。成员: Class targetEntity(),指定目标实体。,指定目标实体。 CascadeType cascade(),指定级联到目标实体的操作类型,
14、指定级联到目标实体的操作类型,相应的操作类型有:相应的操作类型有:ALL、PERSIT(级联新建)、(级联新建)、MERGE(级(级联更新)、联更新)、REMOVE(级联删除)、(级联删除)、REFRESH(级联刷新),(级联刷新),ALL同联合使用其它四个属性效果一样。同联合使用其它四个属性效果一样。 FetchType fetch(),指定成员变量或者是属性是否立即加载还,指定成员变量或者是属性是否立即加载还是延迟加载。是延迟加载。 boolean optional(),指定关联是否可以为空,默认值为,指定关联是否可以为空,默认值为true。 String mappedBy(),指定拥有关
15、系的属性,只需要在关联的反,指定拥有关系的属性,只需要在关联的反向(非拥有者)一端指定使用向(非拥有者)一端指定使用mappedBy成员。成员。一对多及多对一映射一对多及多对一映射一对多及多对一映射:一对多及多对一映射: 关系实例关系实例 :CustomerOrderCompanyEmployeeClassStudent一个一个Demo:以OrderItem和Order 为例 定义一个Session Bean来访问上面的实体Bean,下面是我们定义的业务接口,它定义了三个业务方法,分别是: insertOrder:添加一个定单(带两个定单项)进数据库。 getOrderById:获取指定定单号
16、的定单。 getAllOrder:获取所有定单。 OneToMany注释注释OneToMany注释:用途:定义定义1:N关系中的多值关联。关系中的多值关联。成员: Class targetEntrty(),指定目标实体,定义关系类的类型,默认,指定目标实体,定义关系类的类型,默认是成员属性对应的类类型,所以通常不需要提供定义。是成员属性对应的类类型,所以通常不需要提供定义。 String mappedBy(),定义类之间的双向关系,如果类之间是单,定义类之间的双向关系,如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们向关系,不需要提供定义,如果类和类之间形成双向关系,我们
17、就需要使用这个属性进行定义,否则可能引发数据不一致的问题。就需要使用这个属性进行定义,否则可能引发数据不一致的问题。CascadeType cascade(),该属性成员定义类和类之间的级联,该属性成员定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联对象关系。定义的级联关系将被容器视为对当前类对象及其关联对象采取相同的操作,而且这种关系是递归调用的。采取相同的操作,而且这种关系是递归调用的。FetchType fetch(),指定属性或者是成员变量的值是否立即装载,指定属性或者是成员变量的值是否立即装载,还是延迟装载。可选项包括:还是延迟装载。可选项包括:FetchTy
18、pe.EAGER和和FetchType.LAZY。前者表示立即装载,后者表示延迟。前者表示立即装载,后者表示延迟。 多对多映射多对多映射 多对多映射多对多映射 : 关系实例关系实例 :学生-老师卖家-买家 在实体在实体Bean的代码设计中如果要用到多对多的映射,我们除了像其它的代码设计中如果要用到多对多的映射,我们除了像其它类型的映射所做的那样(使用类型的映射所做的那样(使用Entity注释和注释和Table注释)之外,相信大家注释)之外,相信大家也能够猜到标识多对多的映射注释:也能够猜到标识多对多的映射注释:ManyToMany注释。注释。 下面我们就详细的讲解一下该注释的具体用法。下面我们
19、就详细的讲解一下该注释的具体用法。 ManyToMany注释注释ManyToMany注释: 用途:定义定义M:N关系中的多值关联。如果使用泛型定义关系中的多值关联。如果使用泛型定义Collection参数化类型,则不用指定关联的目标实体类。参数化类型,则不用指定关联的目标实体类。每个每个M:N关系都存在两端:拥有方和非拥有方,或称之关系都存在两端:拥有方和非拥有方,或称之为反向端。中间表是在拥有方指定的。如果是双向关系,为反向端。中间表是在拥有方指定的。如果是双向关系,则每一方都可以成为拥有方。则每一方都可以成为拥有方。成员: Class targetEntity(),指定目标实体(如果使用泛
20、型它将使用,指定目标实体(如果使用泛型它将使用Collection参数化类型);参数化类型); CascadeType cascade(),指定级联到目标实体的操作类型。,指定级联到目标实体的操作类型。类型的参数同样有类型的参数同样有ALL、PERSIT、MERGE、REMOVE、REFRESH参数意义同一对一当中的参数意义同一对一当中的cascade中的参数意义相同。中的参数意义相同。FetchType fetch(),指定属性或者是成员变量的值是否立即加,指定属性或者是成员变量的值是否立即加载或者是延迟加载。载或者是延迟加载。String mappedBy(),指定拥有关系的属性,如果关系
21、不是单向,指定拥有关系的属性,如果关系不是单向的,则必须给出相应的取值。的,则必须给出相应的取值。 组合主键组合主键 在EJB3中定义了两种主键:(1)简单主键)简单主键(2)复合主键)复合主键复合主键类的编写需要符合以下一些要求:复合主键类的编写需要符合以下一些要求: 复合主键类必须是public和具备一个没有参数的constructor。 复合主键类的每个属性变量必须有getter/setter,如果没有,每个属性变量则必须是public或者protected。 复合主键类必须实现java.io.serializable。 复合主键类必须实现equals()和hashcode()方法。 复
22、合主键类中的主键属性变量的名字必须和对应的Entity中主键属性变量的名字相同。一旦主键值设定后,不要修改主键属性变量的值。什么是消息什么是消息消息是软件组件或应用之间的一种通信方法。消息系统是一种对等(peer-to-peer)的系统:消息客户可以向其它客户发送消息,也可以接收来自其它客户的消息。每一个客户和一个消息代理相连,由消息代理提供创建、发送、接收、读取消息的服务。 什么是JMS API Java消息服务是一组Java应用程序接口(Java API),它提供创建、发送、接收、读取消息的服务。由Sun公司和其合作伙伴设计的JMS API定义了一组公共的应用程序接口和相应语法,使得Jav
23、a程序能够和其它消息组件进行通信。 消息驱动消息驱动BEAN介绍介绍消息驱动BEANMDB是设计专门用来处理基于消息请求的组件,如接受是设计专门用来处理基于消息请求的组件,如接受JMS消息或者是消息或者是其它类型的消息。其它类型的消息。MDB的具体特性如下: (1)MDB不存在远程或本地接口。客户不能使用面向对象的风不存在远程或本地接口。客户不能使用面向对象的风格通过远程方法调用接口访问到格通过远程方法调用接口访问到MDB。(2)MDB支持各种用于消息分发的监听器方法。在支持各种用于消息分发的监听器方法。在JMS API消消息监听接口中,仅仅存在单个息监听接口中,仅仅存在单个onMessage
24、()方法,该方法将接收方法,该方法将接收到到JMS消息。消息。(3)MDB监听方法可能不会返回任何值或者异常信息给客户。监听方法可能不会返回任何值或者异常信息给客户。(4)MDB是无状态的,单线程的。是无状态的,单线程的。MDB是不持有会话状态的。是不持有会话状态的。具体的具体的JMS消息类型有:消息类型有:BytesMessage、ObjectMessage、TextMessage、StreamMessage、MapMessage。 开发消息驱动开发消息驱动BeanMDB的开发并不是一件复杂的事情,我们主要是的开发并不是一件复杂的事情,我们主要是基于基于JMS的消息驱动的消息驱动Bean开发
25、,至于其它类型的开发,至于其它类型的MDB开发,大体与此类似。开发,大体与此类似。MDB的Bean类需要实现如下两个接口:javax.jms.MessageListener和可选的和可选的javax.ejb.MessageDrivenBean。javax.jms.MessageListener接口的内容:public interface javax.jms.MessageListenerpublic void onMessage(Message message);一个简单的一个简单的MDB组件组件MessageDriven注释注释 MessageDriven注释:用途:将用途:将Bean类标识
26、为消息驱动类标识为消息驱动Bean。成员:成员: String name(),指定该消息驱动,指定该消息驱动Bean的名字。的名字。 Class messageListenerInterface(),用于指定,用于指定MDB的消息监听的消息监听接口。接口。 ActivationConfigProperty activationConfig(),引用,引用 ActivationConfigProperty注释来配置消息的各种属性,其中注释来配置消息的各种属性,其中destinationType属性指定消息的类型。属性指定消息的类型。 消息的两种类型消息的两种类型Topics和和Queues: T
27、opics(发布发布/订阅订阅):可以有多个客户端。这类似于看电视 Queue(点对点点对点):Queue仅仅允许一个消息传送给一个客户,一个发送者将消息放入队列,接收者从队列中抽取并得到消息,该消息就会在队列中消失 本章总结本章总结实体与实体Bean的概念实体Bean的开发JBoss数据源的配置单表映射的实体Bean的开发对象/关系映射EntityManager API以及实体Bean的生命周期一对一、一对多、多对一、多对多组合主键的概念MDB组件及开发动手实践:映射动手实践:映射多对多映射多对多映射多对多映射应用多对多映射应用 部署本章体验项目中的实体Bean和客户端的测试项目,并启动服务
28、器,之后在浏览器中输入以下地址: “http:/localhost:8080/EjbTest/ManyToManyMapTest.jsp” 如果页面成功运行将会看到如下结果,如图3-1所示:图图3-1 程序运行程序运行结结果果图图 往数据库中插入一条代表Teacher的数据:“张老师”,该数据对应有三条代表Student的数据:“冯小丽”,“刘华”,“李雷”。之后通过方法调用获取这条刚刚插入的数据,并且得到对应的学生数据,最后在页面上将查询结果输出。效果如下:(1)以学生(Student)和老师(Teacher)为例开发一个多对多关系的实体Bean。(2)开发一个Session Bean,并实
29、现以下业务操作:insertTeacher():添加一个教师(包含学生)进数据库。 getTeacherByID():获取指定编号的教师。 getStudentByID():获取指定编号的学生(3)编写客户端测试程序。(1)首先,同一对多和一对一映射类似,我们先开发出其中的一方:学生Student。Student.java:package com.ejb3.entitybean;import java.io.Serializable;import java.util.HashSet;import java.util.Set;import javax.persistence.*;Suppress
30、Warnings(serial)EntityTable(name = Student)public class Student implements Serializableprivate Integer studentid;private String StudentName;private Set teachers = new HashSet();public Student() public Student(String studentName) StudentName = studentName;IdGeneratedValuepublic Integer getStudentid()
31、 return studentid;public void setStudentid(Integer studentid) this.studentid = studentid;Column(nullable=false, length=32)public String getStudentName() return StudentName;public void setStudentName(String studentName) StudentName = studentName;ManyToMany(mappedBy = students)public Set getTeachers()
32、 return teachers;public void setTeachers(Set teachers) this.teachers = teachers;这段代码唯一和前面编写的不同的是ManyToMany注释,该注释表示Student是多对多关系的,mappedBy属性定义了Student为双向关系的维护端。(2)下面来看多对多映射的另外一端:Teacher的代码编写。Teacher.java:package com.ejb3.entitybean;import java.io.Serializable;import java.util.HashSet;import java.util
33、.Set;import javax.persistence.*;SuppressWarnings(serial)EntityTable(name = Teacher)public class Teacher implements Serializableprivate Integer teacherid;private String TeacherName;private Set students = new HashSet();public Teacher() public Teacher(String teacherName) TeacherName = teacherName;IdGen
34、eratedValuepublic Integer getTeacherid() return teacherid;public void setTeacherid(Integer teacherid) this.teacherid = teacherid;Column(nullable=false, length=32)public String getTeacherName() return TeacherName;public void setTeacherName(String teacherName) TeacherName = teacherName;ManyToMany(casc
35、ade = CascadeType.PERSIST, fetch = FetchType.LAZY)JoinTable(name = Teacher_Student,joinColumns = JoinColumn(name = Teacher_ID, referencedColumnName = teacherid),inverseJoinColumns = JoinColumn(name = Student_ID,referencedColumnName =studentid)public Set getStudents() return students;public void setS
36、tudents(Set students) this.students = students;public void addStudent(Student student) if (!this.students.contains(student) this.students.add(student);public void removeStudent(Student student) this.students.remove(student);ManyToMany注释表示Teacher是多对多关系的一端。JoinTable注释描述了多对多关系的数据表关系。name属性指定中间表名称,joinC
37、olumns定义中间表与Teacher表的外键关系。上面的代码中,中间表Teacher_Student的Teacher_ID列是Teacher表的主键列teacherid对应的外键列,inverseJoinColumns属性定义了中间表与另外一端(Student)的外键关系。为了使用上面的实体Bean我们定义一个Session Bean作为它的使用者。(3)下面是Session Bean的业务接口,它定义了三个业务方法insertTeacher(),getTeacherByID(),和getStudentByID(),三个方法的业务功能是: insertTeacher(),添加一个教师(包含学
38、生)进数据库。 getTeacherByID(),获取指定编号的教师。 getStudentByID(),获取指定编号的学生。下面是Session Bean的业务接口及实现类。接口TeacherDAO.java代码如下:package com.ejb3.dao;import com.ejb3.entitybean.Student;public interface TeacherDAO public void insertTeacher(String name, String studentnames);public Teacher getTeacherByID(Integer teacheri
39、d);public Student getStudentByID(Integer studentid);实现类TeacherDAOBean.java代码如下:package com.ejb3.impl;import javax.ejb.Remote;import javax.ejb.Stateless;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import com.ejb3.dao.TeacherDAO;import com.ejb3.entitybean.Student
40、;import com.ejb3.entitybean.Teacher;StatelessRemote (TeacherDAO.class)public class TeacherDAOBean implements TeacherDAO PersistenceContextprotected EntityManager em;public void insertTeacher(String name, String studentnames) Teacher teacher = new Teacher(name);if (studentnames!=null)for(int i=0;istu
41、dentnames.length; i+)teacher.addStudent(new Student(studentnamesi);em.persist(teacher);public Teacher getTeacherByID(Integer teacherid) Teacher teacher= em.find(Teacher.class, teacherid);if (teacher!=null) teacher.getStudents().size();return teacher;public Student getStudentByID(Integer studentid) S
42、tudent student= em.find(Student.class, studentid);if (student!=null) student.getTeachers().size();return student;(4)下面我们编写客户端的JSP测试程序,用于测试编写好的EJB组件。ManyToManyMapTest.JSP程序代码如下:%Properties props = new Properties();props.setProperty(java.naming.factory.initial,org.jnp.interfaces.NamingContextFactory);
43、props.setProperty(java.naming.provider.url, localhost:1099);props.setProperty(java.naming.factory.url.pkgs, org.jboss.naming);InitialContext ctx = new InitialContext(props);try TeacherDAO teacherdao = (TeacherDAO) ctx.lookup(TeacherDAOBean/remote);teacherdao.insertTeacher(张老师,new String 李雷,刘华,冯小丽);T
44、eacher teacher = teacherdao.getTeacherByID(new Integer(1);if (teacher!=null)out.println(= 获取编号为1的老师姓名: + teacher.getTeacherName()+=);Iterator iterator = teacher.getStudents().iterator();while (iterator.hasNext()Student student = (Student) iterator.next();out.println( 他的学生: + student.getSt
45、udentName() +);elseout.println(没有找到编号为1的老师);Student student = teacherdao.getStudentByID(new Integer(1);if (student!=null)out.println(= 获取编号为1的学生姓名: + student.getStudentName()+=);Iterator iterator = student.getTeachers().iterator();while (iterator.hasNext()Teacher tc = (Teacher) iterator.next();out.p
46、rintln( 他的老师:+ tc.getTeacherName() +);elseout.println(没有找到编号为1的学生); catch (Exception e) out.println(e.getMessage();%以上代码首先调用insertTeacher()方法往数据库中插入一条代表Teacher的数据:“张老师”,该数据对应下面又有三条代表Student的数据:“李雷”,“刘华”,“冯小丽”。之后再调用getTeacherByID()方法获取这条刚刚插入的数据,获取对应他的学生的数据,最后在页面上将查询结果输出。成功运行该JSP页面后的结果如图3-7所示: