jpa关联映射
来源:互联网 发布:mac的哪款粉底液好用 编辑:程序博客网 时间:2024/06/05 03:46
jpa的对象关联映射主要通过注解来实现,分为一对多,多对多,一对一,这里只列出我实际项目中用到的,不打算写一些花哨的了。前面定义好了一个User实体类,这里再定义个订单实体类Order,一个用户可以有多个订单,一个订单只属于一个客户。
1、单向多对一
User实体:
@Entity@Table(name = "t_user")public class User { @Id//必须指定主键 @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @Column(name="user_name") private String userName; private String password; private String telephone; private String email; @Column(name="create_time") @Temporal(TemporalType.DATE) private Date createTime; //映射必须定义个空构造器 public User() { } public User(String userName, String password, String telephone, String email, Date createTime) { super(); this.userName = userName; this.password = password; this.telephone = telephone; this.email = email; this.createTime = createTime; } public String getUserInfo(){ return "username:"+this.userName+",email:"+this.email; } get、set、toString方法}
Order实体:
@Entity@Table(name = "t_order")public class Order { @Id @GeneratedValue private Long id; //付款金额 private Integer payment; //支付方式 0:支付宝 1:微信 private Integer channel; //支付状态 private Integer status=0; //创建时间 @Column(name="create_time") @Temporal(TemporalType.DATE) private Date createTime; //单向多对1, @ManyToOne(targetEntity=User.class,fetch=FetchType.LAZY) private User user; public Order() { } public Order(Integer payment, Integer channel, Integer status, Date createTime) { this.payment = payment; this.channel = channel; this.status = status; this.createTime = createTime; } get、set、toString方法}
这里用户没有关联订单,订单关联了用户,所以订单实体有属性
private User user以及get、set方法。
映射单向多对一,使用注解@ManyToOne,使用@JoinColumn(name=”user_id”)指定外键列名为”user_id”,如果不写该注解的话,默认是属性名_id,上面的示例就没有指定外键列名,那么数据库表t_order的外键字段为user_id,但是这时需要指定实体类为User.class,否则执行持久化操作会抛出异常。
(1)、测试单向多对一持久化操作
public class TestMapper1 { private EntityManager entityManager; private EntityManagerFactory entityManagerFactory; private EntityTransaction transaction; @Before public void init(){ entityManagerFactory = Persistence.createEntityManagerFactory("jpa-2"); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); } @After public void destory(){ transaction.commit(); entityManager.close(); entityManagerFactory.close(); } @Test //多对一持久化操作 public void testManyToOne(){ Order order = new Order(1000, 0, 0, new Date()); User user = new User("Lucy", "123456", "13322222226", "lucy@qq.com",new Date()); //订单关联用户 order.setUser(user); entityManager.persist(user); entityManager.persist(order); }}
执行方法,控制台能看到两条插入语句。
查看数据库:
添加了订单记录,关联的用户id为14。
(2)、测试单向多对一查询操作
@Test //多对一查询 public void testManyToOne2(){ Order order = entityManager.find(Order.class, 1l); System.out.println("创建时间:"+order.getCreateTime()); System.out.println("-----------------------------"); System.out.println("用户信息:"+order.getUser()); }
控制台输出:Hibernate: select order0_.id as id1_0_1_, order0_.channel as channel2_0_1_, order0_.create_time as create_t3_0_1_, order0_.payment as payment4_0_1_, order0_.status as status5_0_1_, order0_.user_id as user_id6_0_1_, user1_.id as id1_1_0_, user1_.create_time as create_t2_1_0_, user1_.email as email3_1_0_, user1_.password as password4_1_0_, user1_.telephone as telephon5_1_0_, user1_.user_name as user_nam6_1_0_ from t_order order0_ left outer join t_user user1_ on order0_.user_id=user1_.id where order0_.id=?创建时间:2017-08-28-----------------------------用户信息:User [id=14, userName=Lucy, password=123456, telephone=13322222226, email=lucy@qq.com]
从发生的sql语句能看到,使用一条sql语句使用外连接将order关联的user的信息全部查了出来,也就是使用了立即加载策略。而hibernate的默认加载是懒加载,在jpa中如果要使用懒加载,可以修改@ManyToOne的属性fetch
分为:
FetchType.EAGER:立即加载(默认)
FetchType.LAZY:延迟加载
@ManyToOne(targetEntity=User.class,fetch=FetchType.LAZY) private User user;
这时候再执行上面的查询语句
Hibernate: select order0_.id as id1_0_0_, order0_.channel as channel2_0_0_, order0_.create_time as create_t3_0_0_, order0_.payment as payment4_0_0_, order0_.status as status5_0_0_, order0_.user_id as user_id6_0_0_ from t_order order0_ where order0_.id=?创建时间:2017-08-28-----------------------------Hibernate: select user0_.id as id1_1_0_, user0_.create_time as create_t2_1_0_, user0_.email as email3_1_0_, user0_.password as password4_1_0_, user0_.telephone as telephon5_1_0_, user0_.user_name as user_nam6_1_0_ from t_user user0_ where user0_.id=?用户信息:User [id=14, userName=Lucy, password=123456, telephone=13322222226, email=lucy@qq.com]
可以发现发送了两条sql,只有需要用到user的时候才进行查询
(3)、测试单向多对一删除
单向多对一删除如果先删除多的一方再删除一的一方是没问题的,但是先删除一的一方肯定是不能执行的,因为一的一方的主键为多的一方的外键,但是可以通过修改@OneToMany的cascade来修改默认的删除策略(级联删除),在一对多的测试时候演示。
@Test //多对1删除 public void testManyToOne3(){ Order order = entityManager.find(Order.class, 1l); entityManager.remove(order); }
(4)、测试单向多对1修改
@Test //多对1修改 public void testManyToOne4(){ Order order = entityManager.find(Order.class, 1l); order.getUser().setUserName("jackson"); }
输出:三条sql语句,第一条查询订单信息,由于懒加载第二条查询用户信息,最后执行update操作更新用户名。
2、单向一对多
修改User实体类,关联订单,一个用户拥有多个订单,一对多
添加属性
@JoinColumn(name="user_id") @OneToMany private Set<Order> orders = new HashSet<>(); get,set方法
注:外键是唯一的,知道外键列名必须跟Order实体类一致。
(1)、测试单向一对多持久化操作
@Test public void testOneToMany(){ Order order = new Order(1000, 0, 0, new Date()); Order order2 = new Order(999, 1, 0, new Date()); User user = new User("Hession", "123456", "15822222226", "hession@qq.com",new Date()); //用户关联订单 user.getOrders().add(order); user.getOrders().add(order2); //持久化操作 entityManager.persist(user); entityManager.persist(order); entityManager.persist(order2); }
查看控制台输出语句:
一共五条:前三条是插入用户信息和两条订单记录语句。后两条是修改订单信息,添加两条订单记录的user_id,因为这时候外键由一的一方维护,必须一的一方插入后才能拿到id,然后才能设置外键(user_id)。
三条插入语句(一个用户2个订单)两条更新语句如下:Hibernate: update t_order set user_id=? where id=?Hibernate: update t_order set user_id=? where id=?
(2)、测试单向一对多查询
@Test public void testManyToOne0(){ User user = entityManager.find(User.class, 16l); System.out.println(user); System.out.println("---------------"); System.out.println(user.getOrders().size()); }
控制台输出:Hibernate: select user0_.id as id1_1_0_, user0_.create_time as create_t2_1_0_, user0_.email as email3_1_0_, user0_.password as password4_1_0_, user0_.telephone as telephon5_1_0_, user0_.user_name as user_nam6_1_0_ from t_user user0_ where user0_.id=?User [id=16, userName=Hession, password=123456, telephone=15822222226, email=hession@qq.com]---------------Hibernate: select orders0_.user_id as user_id6_1_1_, orders0_.id as id1_0_1_, orders0_.id as id1_0_0_, orders0_.channel as channel2_0_0_, orders0_.create_time as create_t3_0_0_, orders0_.payment as payment4_0_0_, orders0_.status as status5_0_0_, orders0_.user_id as user_id6_0_0_ from t_order orders0_ where orders0_.user_id=?2
从输出能看到,存在懒加载问题,默认策略跟多对一是相反的,同样可以修改@OneToMany的fetch来修改加载策略。
(3)、测试单向一对多删除
单向一对多删除,可以直接对一的一端删除
@Test public void testOneToMany2(){ User user = entityManager.find(User.class, 16l); entityManager.remove(user); }
控制台输出:Hibernate: select user0_.id as id1_1_0_, user0_.create_time as create_t2_1_0_, user0_.email as email3_1_0_, user0_.password as password4_1_0_, user0_.telephone as telephon5_1_0_, user0_.user_name as user_nam6_1_0_ from t_user user0_ where user0_.id=?Hibernate: update t_order set user_id=null where user_id=?Hibernate: delete from t_user where id=?
从输出显而易见,先会将t_order表中关联的记录的user_id设为null,这时就不再关联该用户了,接着删除该用户。
注:如果想将该用户下的订单也删除,那么可以通过casecade属性来设置级联删除
@JoinColumn(name="user_id") @OneToMany(cascade=CascadeType.REMOVE) private Set<Order> orders = new HashSet<>();
(4)、测试单向一对多修改
@Test public void testOneToMany3(){ User user = entityManager.find(User.class, 18l); user.getOrders().iterator().next().setPayment(1111); }
获取持久化状态的对象之间设置属性即可。
3、双向多对一
双向多对一关系,需要留意的是最好让一方维护外键,而外键是在多的一方,一般由多的一方维护外键即可。操作的话跟一对多,多对一是一样。
可以在一的一方来设置,放弃外键维护。
//@JoinColumn(name="user_id")@OneToMany(fetch=FetchType.LAZY,cascade={CascadeType.REMOVE},mappedBy="user")private Set<Order> orders = new HashSet<>();
mappedBy=”user”可以理解为一的一方放弃维护外键,外键由多的一方维护,user表示的是多的一方所持有的一的一方的属性。
一旦用了mappedBy,那么便不能再使用@JoinColumn(name=”user_id”)注解。
4、单向一对一
还是用上面的订单作为一方实体类,在自动售货机上使用扫描支付,那么订单和商品就是一对一的关系,添加商品实体类Goods。
由商品实体类来维护关联关系(没必要两边都维护,@OneToOne注解在一边使用即可,包括前面的一对多多对一,通常注解在一边使用即可)。
@Entity@Table(name="t_goods")public class Goods { @Id @GeneratedValue private long id; private String name; private Integer price; @Column(name="create_time") @Temporal(TemporalType.DATE) private Date createTime; //一对一关联 @OneToOne private Order order; public Goods(String name, Integer price, Date createTime) { this.name = name; this.price = price; this.createTime = createTime; } public Goods() { } get、set方法}
(1)、测试持久化操作
public void testOneToOne(){ Order order = new Order(2222, 1, 0, new Date()); Goods goods = new Goods("健力宝", 250, new Date()); //商品关联订单 goods.setOrder(order); //持久化操作 entityManager.persist(order); entityManager.persist(goods); }
控制台会打印两条插入到两个表t_order,t_goods记录的语句,查看数据库表
插入了数据且已经关联。
至于多对多也是类似,实际使用中建议注解@ManyToMany使用在一边即可(多对多跟hibernate一样必然有一方要放弃维护外键)。
总结:不管是一对多还是多对一,还是一对一还是多对多,或是单向双向。最后使用的时候一般都可以只用单向,即在一方使用注解,最后需要的数据都是能查询到的而不用像学习的时候整的过于繁琐。
- JPA关联关系映射
- jpa关联映射
- JPA表映射-关联
- JPA关联映射 - 一对一映射
- Hibernate JPA 之关联映射
- JPA-4 映射关联关系
- JPA unidirection Relation Mapping(JPA关联映射)
- JPA关联映射 - 一对多映射
- JPA关联映射 - 多对多映射
- JPA关联映射 - 复合主键映射
- JPA实体关联关系映射之概述
- JPA映射关联关系-单向一对多
- JPA映射关联关系-双向一对多
- JPA映射关联关系-双向一对一
- JPA学习笔记【五】【映射关联关系】
- JPA bidirection Relation Mapping(JPA双向关联映射)
- Hibernate,JPA 对象关系映射之关联关系映射策略
- JPA 对象关系映射之关联关系映射策略
- 【POJ】3662
- 合法括号序列判断
- OSG-更新和回调
- 1019
- 【STL】反向迭代器
- jpa关联映射
- 遍历目录下所有文件夹和文件-------递归删除空目录
- 程序员的自我修养1
- 一致性hash算法的java实现
- Android Api demo系列(11) (App>Alarm警报管理员)
- 数据结构封装之 《SeqList顺序表》
- 2017搜狐笔试题(Java实现)
- NET如何做出老效果,表格操作很简单。
- POJ1459-Power Network