jpa关联映射

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<>();
    getset方法

注:外键是唯一的,知道外键列名必须跟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一样必然有一方要放弃维护外键)。

总结:不管是一对多还是多对一,还是一对一还是多对多,或是单向双向。最后使用的时候一般都可以只用单向,即在一方使用注解,最后需要的数据都是能查询到的而不用像学习的时候整的过于繁琐。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值