一对一关联查询注解@OneToOne的实例详解(一)

转载自:
https://www.cnblogs.com/boywwj/p/8092915.html

一对一关联查询注解@OneToOne的实例详解
  表的关联查询比较复杂,应用的场景很多,本文根据自己的经验解释@OneToOne注解中的属性在项目中的应用。本打算一篇博客把增删改查写在一起,但是在改的时候遇到了一些问题,感觉挺有意思,所以写下第二篇专门讲修改。

一、单向@OneToOne实例详解

假设一个场景,一个人只能领养一只宠物,根据人能够找到宠物,并且查看宠物的信息,关系是单向的。

创建人与宠物的数据表结构。下载地址:Person,Pet数据库建表。

创建实体。

Person.java


package com.my.model;
 
import java.io.Serializable;
 
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
 
import org.hibernate.annotations.Cascade;
import org.springframework.beans.factory.annotation.Autowired;
 
@Entity
@Table(name = "person")
public class Person  implements Serializable{
    @Id
    // id自动生成
    @GeneratedValue
    @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;
     
    //cascade:表的级联操作
    @OneToOne(fetch=FetchType.LAZY,cascade = CascadeType.ALL) //JPA注释: 一对一 关系
     
    //referencedColumnName:参考列名,默认的情况下是列表的主键
    //nullable=是否可以为空,
    //insertable:是否可以插入,
    //updatable:是否可以更新
    // columnDefinition=列定义,
    //foreignKey=外键
    @JoinColumn(name="pet_id",referencedColumnName="id",nullable=false)
    private Pet pet;
 
     
 
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", pet=" + pet + "]";
    }
     
}

Pet.java

package com.my.model;
 
import java.io.Serializable;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table(name = "pet")
public class Pet  implements Serializable{
    @Id
    // id自动生成
    @GeneratedValue
    @Column(name = "id")
    private Long id;
    @Column(name = "pet_name")
    private String petName;
    @Column(name = "pet_class")
    private String petClass;
 
    //省略set,get方法。
 
    @Override
    public String toString() {
        return "Pet [id=" + id + ", petName=" + petName + ", petClass="
                + petClass + "]";
    }
 
}  

注解@OneToOne的接口定义如下:

public interface OneToOne extends Annotation {
 
    public abstract Class targetEntity();
 
    public abstract CascadeType[] cascade();
 
    public abstract FetchType fetch();
 
    public abstract boolean optional();
 
    public abstract String mappedBy();
 
    public abstract boolean orphanRemoval();
}
 

注解@OneToOne的属性:

cascade:关联属性,这个属性定义了当前类对象操作了之后,级联对象的操作。本例中定义了:CascadeType.ALL,当前类增删改查改变之后,关联类跟着增删改查。

fetch属性:FetchType类型的属性。可选择项包括:FetchType.EAGER 和FetchType.LAZY。 FetchType.EAGER表示关系类(本例是OrderItem类)在主类加载的时候同时加载,FetchType.LAZY表示关系类在被访问时才加载。默认值是FetchType.LAZY。

mappedBy:拥有关联关系的域,如果关系是单向的就不需要,双向关系表,那么拥有关系的这一方有建立、解除和更新与另一方关系的能力,而另一方没有,只能被动管理,这个属性被定义在关系的被拥有方。双向@OneToOne,双向@OneToMany,双向@ManyToMany。

注解@JoinColumn的接口定义:

public interface JoinColumn extends Annotation {
 
    public abstract String name();
 
    public abstract String referencedColumnName();
 
    public abstract boolean unique();
 
    public abstract boolean nullable();
 
    public abstract boolean insertable();
 
    public abstract boolean updatable();
 
    public abstract String columnDefinition();
 
    public abstract String table();
 
    public abstract ForeignKey foreignKey();
}

注解@JoinColumn的属性:

name属性:外键列的名称,默认情况下是:引用实体的字段名称 +“_”+ 被引用的主键列的名称。一般也可以自定义,一般见名知意,就可以采用默认值。

referencedColumnName属性:参考列,默认值是关联表的主键。例如你可以定义pet_name为参考列,那么就会将pet的name的值关联到这一列。

创建类:TableRelationController

package com.my.controller;
 
import javax.annotation.Resource;
 
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.alibaba.fastjson.JSONObject;
import com.my.model.GoodInfoEntity;
import com.my.service.TableRelationService;
 
/**
 * 用于测试表的七种对应关系
 * @author by_ww
 *
 */
@RestController
@RequestMapping(value = "/tableRelation")
public class TableRelationController {
     
    @Resource
    private TableRelationService tableRelationService;
     
    // 增加
     @RequestMapping(value = "/save")
        public Long save(@RequestBody JSONObject record) throws Exception
        {
            return tableRelationService.save(record);
        }
    // 查询
     @RequestMapping(value = "/query")
        public JSONObject query(@RequestBody JSONObject record) throws Exception
        {
            return tableRelationService.getPet(record);
        }
     // 删除
     @RequestMapping(value = "/delete")
        public Long delete(@RequestBody JSONObject record) throws Exception
        {
            return tableRelationService.delete(record);
        }
      
     // 更改
     @RequestMapping(value = "/update")
        public Long update(@RequestBody JSONObject record) throws Exception
        {
            return tableRelationService.update(record);
        }
}
 

创建TableRelationService类:

package com.my.service;
 
import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.my.dao.PersonJPA;
import com.my.dao.PetJPA;
import com.my.model.Person;
import com.my.model.Pet;
 
@Service
public class TableRelationService {
     
    @Resource
    private PersonJPA personJPA;
     
    @Resource
    private PetJPA petJPA;
     
  private SessionFactory sessionFactory;
 
  @Autowired
  public void SomeService(EntityManagerFactory factory) {
    if(factory.unwrap(SessionFactory.class) == null){
      throw new NullPointerException("factory is not a hibernate factory");
    }
    this.sessionFactory = factory.unwrap(SessionFactory.class);
  }
     
      
    public Long save(JSONObject record) {
         
        // 组装person
        Person person = new Person();
        person.setName(record.getString("personName"));
        JSONObject petObj = record.getJSONObject("pet");
        if (null != petObj) {
            Pet pet = new Pet();
            pet.setPetName(petObj.getString("petName"));
            pet.setPetClass(petObj.getString("petClass"));
             
            person.setPet(pet);
        }
        personJPA.save(person);
         
        return 4l;
    }
 
    public JSONObject getPet(JSONObject record) {
         
        Person person = personJPA.findOne(record.getLongValue("id"));
        System.out.println(person.toString());
        return (JSONObject) JSON.toJSON(person);
    }
 
    public Long delete(JSONObject record) {
        personJPA.delete(record.getLongValue("id"));
        return 4l;
    }
      @Transactional
    public Long update(JSONObject record) {
         
         Session session = sessionFactory.getCurrentSession();
//       Session     session = sessionFactory.openSession();
         session.beginTransaction();
          
         Person personRecord = session.get(Person.class, record.getLongValue("id"));
          
        personRecord.setName(record.getString("personName"));
         
        JSONObject petObject = record.getJSONObject("pet");
         
        if (petObject != null) {
             // 如果这里的pet为空
             Pet petRecord = null;
            if (personRecord.getPet() != null) {
                petRecord = session.get(Pet.class, personRecord.getPet().getId());
            }
          
          petRecord.setPetName(petObject.getString("petName"));
          petRecord.setPetClass(petObject.getString("petClass"));
           
        }
        personJPA.save(personRecord);
        return 4l;
    }
 
}

注意:这里关联表更改的时候要注意,如果没有配置好会出现异常。

1
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
这是在spring的事务实现中需要判断当前线程中的事务是否同步,而没有事务的时候,那个判断是否同步的方法会因为get返回初始的null值而返回false,最终导致throw一个Could not obtain transaction-synchronized Session for current thread的异常,解决方法有两个:

1)加事物控制。

@Transactional

2)重新生成session。

Session  session = sessionFactory.openSession();

配置文件中:

spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
  

测试:postMan发送请求:

增加:

{
 
"personName":"Steven",
"pet" : {
    "petName":"旺旺",
    "petClass":"dog"
    }
 
}

查询:

{
"id" : 19
}
 
{
    "id": 19,
    "pet": {
        "id": 19,
        "petClass": "dog",
        "petName": "旺旺"
    },
    "name": "Steven"
}

删除:

{
"id" : 19
}  
 

更改:

这里更改了petName,petClass

{
"id" : 19,
"personName":"Steven",
  "pet" :{
    "petName" : "steven4",
    "petClass" : "cow" 
  }
}

问题:

在更新的时候存在这样的问题,如果刚开是插入数据的时候,没有插入从表Pet的数据。

{
 
"personName":"King"
 
}

此时如果想在更新数据,给King添加一个pet数据,那么就会一直插入。

{
"id" : 20,
"personName": "King",
  "pet" :{
    "petName" : "阿旺",
    "petClass" : "cow" 
  }
}

这是因为程序在执行save操作的时候,默默的执行了下面的语句。

Hibernate: select person0_.id as id1_2_1_, person0_.name as name2_2_1_, person0_.pet_id as pet_id3_2_1_, pet1_.id as id1_3_0_, pet1_.pet_class as pet_clas2_3_0_, pet1_.pet_name as pet_name3_3_0_ from person person0_ inner join pet pet1_ on person0_.pet_id=pet1_.id where person0_.id=?
Hibernate: insert into pet (pet_class, pet_name) values (?, ?)
Hibernate: insert into person (name, pet_id) values (?, ?)
Hibernate: insert into pet (pet_class, pet_name) values (?, ?)
Hibernate: update person set name=?, pet_id=? where id=?

第一条语句查出来是空,所以会执行插入操作。其实这边也没有完全搞懂,欢迎留言!

这个问题的解决方式,请参考双向一对一映射@OneToOne。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值