1.关系/对象映射
1.1一对多或多对一映射
整个代码如下:
@SuppressWarnings("serial") @Entity @Table(name = "Orders") public class Order implements Serializable { private Integer orderid; private Float amount; private Set orderItems = new HashSet (); private Date createdate; @Id @GeneratedValue public Integer getOrderid() { return orderid; } public void setOrderid(Integer orderid) { this.orderid = orderid; } public Float getAmount() { return amount; } public void setAmount(Float amount) { this.amount = amount; } @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY) @OrderBy(value = "id ASC") public Set getOrderItems() { return orderItems; } public void setOrderItems(Set orderItems) { this.orderItems = orderItems; } @Temporal(value = TemporalType.TIMESTAMP) public Date getCreatedate() { return createdate; } public void setCreatedate(Date createdate) { this.createdate = createdate; } public void addOrderItem(OrderItem orderitem) { if (!this.orderItems.contains(orderitem)) { this.orderItems.add(orderitem); orderitem.setOrder(this); } } public void removeOrderItem(OrderItem orderitem) { orderitem.setOrder(null); this.orderItems.remove(orderitem); } } @SuppressWarnings("serial") @Entity @Table(name = "OrderItems") public class OrderItem implements Serializable { private Integer id; private String productname; private Float price; private Order order; public OrderItem() { } public OrderItem(String productname, Float price) { this.productname = productname; this.price = price; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getProductname() { return productname; } public void setProductname(String productname) { this.productname = productname; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } @ManyToOne(cascade = CascadeType.REFRESH, optional = false) @JoinColumn(name = "order_id") public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } } @Stateless @Remote(OrderDAO.class) public class OrderDAOBean implements OrderDAO { @PersistenceContext protected EntityManager em; public void insertOrder() { Order order = new Order(); order.setCreatedate(new Date()); order.addOrderItem(new OrderItem("笔记本电脑", new Float(13200.5))); order.addOrderItem(new OrderItem("U盘", new Float(620))); order.setAmount(new Float(13200.5 + 620)); em.persist(order); } public Order getOrderByID(Integer orderid) { Order order = em.find(Order.class, orderid); order.getOrderItems().size(); // 因为是延迟加载,通过执行size()这种方式获取订单下的所有订单项 return order; } public List getAllOrder() { Query query = em .createQuery("select DISTINCT o from Order o inner join fetch o.orderItems order by o.orderid"); List result = query.getResultList(); return result; } } import com.OrderDAO; public class OrderDAOTest extends TestCase { private OrderDAO orderDAO; protected void setUp() throws Exception { orderDAO=(OrderDAO) EJBFactory.getEJB("OrderDAOBean/remote"); } public void testGetOrderByID(){ orderDAO.insertOrder(); assertEquals(1,orderDAO.getAllOrder().size()); } }
如果应用采用的数据库是Mysql,当映射的表名或列名与数据库保留字同名时,持久化引掣转绎后的SQL 在执行时将会出错。如:表名为order 这样就会出错,可用可以用``字符把order 括起, @Table(name = "`Order`")
a.注释@OneToMany(mappedBy="order",cascade =CascadeType.ALL, fetch = FetchType.LAZY)指明Order 与OrderItem 关联关系为一对多关系,下面是@OneToMany注释的属性介绍:
1>targetEntity
Class 类型的属性。定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义。
2>mappedBy
String 类型的属性。定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据一致性的问题。等于维护另一端对应的属性名。
3>cascade
CascadeType[]类型。
该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。举个例子:Order 和OrderItem 有级联关系,那么删除Order 时将同时删除它所对应的OrderItem 对象。而如果OrderItem 还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。cascade 的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。
4>fatch
FetchType 类型的属性。
可选择项包括:FetchType.EAGER 和FetchType.LAZY。前者表示关系类(本例是OrderItem 类)在主类(本例是Order类)加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是FetchType. LAZY。@OrderBy(value = "id ASC")注释指明加载OrderItem 时按id 的升序排序
b. @ManyToOne 指明OrderItem 和Order 之间为多对一关系,多个OrderItem 实例关联的都是同一个Order 对象。
@ManyToOne 注释有四个属性:targetEntity、cascade、fetch 和optional,前三个属性的具体含义和@OneToMany注释的同名属性相同,但@ManyToOne 注释的fetch 属性默认值是FetchType.EAGER。optional 属性是定义该关联类是否必须存在,值为false 时,关联类双方都必须存在,如果关系被维护端不存在,查询的结果为null。值为true 时, 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护端中指向关系被维护端的属性为null。optional 属性的默认值是true。optional 属性实际上指定关联类与被关
联类的join 查询关系,如optional=false 时join 查询关系为inner join, optional=true 时join 查询关系为left join.
1.2 一对一映射
代码如下:
package com; import java.io.Serializable; import java.util.Date; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; @SuppressWarnings("serial") @Entity @Table(name = "Person") public class Person implements Serializable { private Integer personid; private String name; private boolean sex; private Short age; private Date birthday; private IDCard idcard; @Id @GeneratedValue(strategy = GenerationType.AUTO) //主键自动生成方式 public Integer getPersonid() { return personid; } public void setPersonid(Integer personid) { this.personid = personid; } @Column(nullable = false, length = 32) public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(nullable = false) public boolean getSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } @Column(nullable = false) public Short getAge() { return age; } public void setAge(Short age) { this.age = age; } @Temporal(value = TemporalType.DATE) //@Temporal 注释用来指定java.util.Date 或java.util.Calendar 属性与数据库类型date,time 或timestamp 中的那一种 //类型进行映射,DATE, //代表date 类型;TIME, //代表时间类型;TIMESTAMP //代表时间戳类型 public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Transient public String getFristName() { return "li"; } @OneToOne(optional = true,cascade = CascadeType.ALL, mappedBy = "person") //optional = true 设置idcard 属性可以为null,也就是允讦没有身份证,未成年人就是没有身份证的。 public IDCard getIdcard() { return idcard; } public void setIdcard(IDCard idcard) { this.idcard = idcard; } } package com; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; @SuppressWarnings("serial") @Entity @Table(name = "IDCard") public class IDCard implements Serializable { private Integer id; private String cardno; private Person person; public IDCard() { } public IDCard(String cardno) { this.cardno = cardno; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(nullable = false, length = 18, unique = true) public String getCardno() { return cardno; } public void setCardno(String cardno) { this.cardno = cardno; } @OneToOne(optional = false, cascade = CascadeType.REFRESH)//optional = false 设置person 属性值不能为null @JoinColumn(name = "Person_ID", referencedColumnName = "personid", unique = true) public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } } @Stateless @Remote( { OneToOneDAO.class }) public class OneToOneDAOBean implements OneToOneDAO { @PersistenceContext protected EntityManager em; public void insertPerson(String name, boolean sex, short age, Date birthday, String cardID) { Person person = new Person(); person.setName(name); person.setSex(sex); person.setAge(Short.valueOf(age)); person.setBirthday(birthday); IDCard idcard = new IDCard(cardID); idcard.setPerson(person); person.setIdcard(idcard); em.persist(person); } public Person getPersonByID(Integer personid) { Person person = em.find(Person.class, personid); return person; } public void updatePersonInfo(Integer personid, String newname, String newIDcard) { Person person = em.find(Person.class, personid); if (person != null) { person.setName(newname); if (person.getIdcard() != null) { person.getIdcard().setCardno(newIDcard); } em.merge(person); } } public void deletePerson(Integer personid) { Person person = em.find(Person.class, personid); if (person != null) em.remove(person); } } import com.dao.OneToOneDAO; public class OneToOneTest extends TestCase { private OneToOneDAO oneToOneDAO; protected void setUp() throws Exception { oneToOneDAO=(OneToOneDAO) EJBFactory.getEJB("OneToOneDAOBean/remote"); } public void testGetPersonByID(){ //oneToOneDAO.insertPerson("测试",true,(short)23,new Date(),"dddddddd"); //oneToOneDAO.insertPerson("测试1",true,(short)23,new Date(),"d111ddddddd"); assertEquals("dddddddd",oneToOneDAO.getPersonByID(1).getIdcard().getCardno()); } public void testDeletePerson(){ oneToOneDAO.deletePerson(1); } }
1.3 多对多映射
多对多映射采取中间表连接的映射策略,建立的中间表将分别引入两边的主键作为外键。
@SuppressWarnings("serial") @Entity @Table(name = "Teacher") public class Teacher implements Serializable { private Integer teacherid; private String TeacherName; private Set students = new HashSet (); public Teacher() { } public Teacher(String teacherName) { TeacherName = teacherName; } @Id @GeneratedValue public 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(cascade = 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 setStudents(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); } } @SuppressWarnings("serial") @Entity @Table(name = "Student") public class Student implements Serializable { private Integer studentid; private String StudentName; private Set teachers = new HashSet (); public Student() { } public Student(String studentName) { StudentName = studentName; } @Id @GeneratedValue public Integer getStudentid() { 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() { return teachers; } public void setTeachers(Set teachers) { this.teachers = teachers; } } @Stateless @Remote( { TeacherDAO.class }) public class TeacherDAOBean implements TeacherDAO { @PersistenceContext protected EntityManager em; public void insertTeacher(String name, String[] studentnames) { Teacher teacher = new Teacher(name); if (studentnames != null) { for (int i = 0; i < studentnames.length; i++) { teacher.addStudent(new Student(studentnames[i])); } } 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) { Student student = em.find(Student.class, studentid); if (student != null) student.getTeachers().size(); return student; } } public class TeacherDAOTest extends TestCase { private TeacherDAO teacherDAO; protected void setUp() throws Exception { teacherDAO=(TeacherDAO) EJBFactory.getEJB("TeacherDAOBean/remote"); } public void testGetStudentByID(){ teacherDAO.insertTeacher("小五",new String[]{"线三","要发了"}); assertEquals("线三",teacherDAO.getStudentByID(1).getStudentName()); } }
@ManyToMany 注释表示Teacher 是多对多关系的一端。@JoinTable 描述了多对多关系的数据表关系。name 属性指定中间表名称,joinColumns 定义中间表与Teacher 表的外键关系。上面的代码中,中间表Teacher_Student的Teacher_ID 列是Teacher 表的主键列对应的外键列,inverseJoinColumns 属性定义了中间表与另外一端(Student)的外键关系。