zoukankan      html  css  js  c++  java
  • 一对一关联查询注解@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;
    	}
    
    }

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

    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。

  • 相关阅读:
    winform导入导出excel,后台动态添加控件
    asp.net 导入excel文件
    asp.net gridview动态添加列,并获取其数据;
    中转Http请求
    窗体托盘后台运行
    后台程序完成指定任务
    死锁查看
    异步等待(ManualResetEvent
    C#后台程序重启IIS,发邮件通知
    mybatis入门视频总结
  • 原文地址:https://www.cnblogs.com/boywwj/p/8092915.html
Copyright © 2011-2022 走看看