一對一關聯查詢注解@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。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM