1. 程式人生 > >@OneToOne例項詳解

@OneToOne例項詳解

主要是這一截:

    @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;

表的關聯查詢比較複雜,應用的場景很多,本文根據自己的經驗解釋@OneToOne註解中的屬性在專案中的應用。本打算一篇部落格把增刪改查寫在一起,但是在改的時候遇到了一些問題,感覺挺有意思,所以寫下第二篇專門講修改。

一、單向@OneToOne例項詳解

假設一個場景,一個人只能領養一隻寵物,根據人能夠找到寵物,並且檢視寵物的資訊,關係是單向的。

建立人與寵物的資料表結構。下載地址:Person,Pet資料庫建表。

建立實體。

Person.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

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的介面定義如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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的介面定義:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

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類:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

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)加事物控制。

1

@Transactional

2)重新生成session。

1

Session  session = sessionFactory.openSession();

配置檔案中:

1

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

  

測試:postMan傳送請求:

增加:

1

2

3

4

5

6

7

8

9

{

 

"personName":"Steven",

"pet" : {

    "petName":"旺旺",

    "petClass":"dog"

    }

 

}

 

查詢:

1

2

3

{

"id" 19

}

 

1

2

3

4

5

6

7

8

9

{

    "id"19,

    "pet": {

        "id"19,

        "petClass""dog",

        "petName""旺旺"

    },

    "name""Steven"

}

刪除:

1

2

3

{

"id" 19

}  

 

更改:

這裡更改了petName,petClass

1

2

3

4

5

6

7

8

{

"id" 19,

"personName":"Steven",

  "pet" :{

    "petName" "steven4",

    "petClass" "cow" 

  }

}

  

問題:

在更新的時候存在這樣的問題,如果剛開是插入資料的時候,沒有插入從表Pet的資料。

1

2

3

4

5

{

 

"personName":"King"

 

}

此時如果想在更新資料,給King新增一個pet資料,那麼就會一直插入。

1

2

3

4

5

6

7

8

{

"id" 20,

"personName""King",

  "pet" :{

    "petName" "阿旺",

    "petClass" "cow" 

  }

}

這是因為程式在執行save操作的時候,默默的執行了下面的語句。

1

2

3

4

5

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。

 

原文連結:https://www.cnblogs.com/boywwj/p/8092915.html