使用JPA進(jìn)行數(shù)據(jù)查詢和關(guān)聯(lián)查詢

使用JPA進(jìn)行數(shù)據(jù)查詢和關(guān)聯(lián)查詢

在開(kāi)發(fā)中經(jīng)常會(huì)使用mybatis、jpa等框架來(lái)解決O/R映射技術(shù)實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)。本文主要介紹使用JPA實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)。
通常在關(guān)聯(lián)查詢的時(shí)候,表與表之前存在的關(guān)系有</br>
OneToOne</br>
OneToMany</br>
ManyToOne</br>
ManyToMany</br>
想要理清兩個(gè)表之間的關(guān)系需要根據(jù)實(shí)際場(chǎng)景進(jìn)行區(qū)分酥诽。在建立實(shí)體類的時(shí)候,需要知道哪一個(gè)是主體皱埠。通常情況下一對(duì)多和多對(duì)一始終是以多的一方為主體的肮帐。注解在使用中“始終在非主體的一方標(biāo)記自己在主體中的名稱”

基本數(shù)據(jù)查詢

舉例:有如下幾個(gè)表的關(guān)系,表之間的ER圖如下:


1
1
image.png

一對(duì)一

Student和Sore對(duì)應(yīng)的關(guān)系,score類如下:</br>

package spring.demo.security.entity.test;

import javax.persistence.*;

/**
 * Created by td on 2017/10/12.
 */
@Entity
@Table(name = "score")
public class Score {
    @Id
    @GeneratedValue
    private Integer id;

    @Column(name = "chinese_score")
    private Integer chinese;

    @Column(name = "math_score")
    private Integer math;

    //通常情況下score和student中边器,認(rèn)為studnet是主體训枢,我們需要在sore非主體中標(biāo)記他在主體中的名字
    @OneToOne(mappedBy = "score")
    private Student student;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getChinese() {
        return chinese;
    }

    public void setChinese(Integer chinese) {
        this.chinese = chinese;
    }

    public Integer getMath() {
        return math;
    }

    public void setMath(Integer math) {
        this.math = math;
    }
}

student類如下:</br>

package spring.demo.security.entity.test;

import javax.persistence.*;

/**
* Created by td on 2017/10/12.
*/
@Entity
@Table(name = "student")
public class Student {

  @Id
  @GeneratedValue
  private Integer id;


  private String username;

  @OneToOne
  private Score score;

  public Integer getId() {
      return id;
  }

  public void setId(Integer id) {
      this.id = id;
  }

  public String getUsername() {
      return username;
  }

  public void setUsername(String username) {
      this.username = username;
  }
}
```</br>
這兩個(gè)類中需要區(qū)分誰(shuí)是主體,按照?qǐng)鼍袄斫釹tudent和Score會(huì)認(rèn)為Student,所以我們需要在非主體的Score中指定其在Student中的名字忘巧。</br>
此外還可以設(shè)置級(jí)聯(lián)恒界,只需要在注解中增加參數(shù)(cascade = CascadeType.REMOVE ),必需要在主體一側(cè)添加級(jí)聯(lián)砚嘴。(一般情況下不用)<br>
#### 一對(duì)多(多對(duì)一)
以下建立student和school的關(guān)系,student是主體,下面是School類代碼<br>

```java
package spring.demo.security.entity.test;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;

/**
* Created by td on 2017/10/12.
*/
public class School {
  @Id
  @GeneratedValue
  private Integer id;

  private String name;

  @OneToMany(mappedBy = "school")
   private List<Student> students;

  public Integer getId() {
      return id;
  }

  public void setId(Integer id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public List<Student> getStudents() {
      return students;
  }

  public void setStudents(List<Student> students) {
      this.students = students;
  }
}

在Student中加入school類的十酣,多對(duì)一關(guān)系,并且需要在school類中指定其在student中的名字

// student類中加入school類,多對(duì)一關(guān)系
    @ManyToOne
    private School school;
// school類中加入student并指明在student中的名字

    @OneToMany(mappedBy = "school")
     private List<Student> students;

多對(duì)多

Subject和Student之間是多對(duì)多的關(guān)系际长,下面建立subject類

package spring.demo.security.entity.test;

import javax.persistence.*;
import java.util.List;

/**
 * Created by td on 2017/10/12.
 */
@Entity
@Table(name = "subject")
public class Subject {
    @Id
    @GeneratedValue
    private Integer id;

    @Column(length = 10)
    private String name;

    @ManyToMany(mappedBy = "subjects")
    private List<Student> students;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在student類中加入

   @ManyToMany
    private List<Subject> subjects;

基本查詢

基本查詢通過(guò)繼承JpaRepository<T,key>就可以了,如果符合規(guī)范可以不用寫實(shí)現(xiàn)

public interface StudentRepository extends JpaRepository<Student,Integer> {
    
}

通過(guò)以上就可以進(jìn)行基本的增刪改查
如果JpaRepository條件不能滿足需求耸采,也可以自定義Repository自定義條件:
舉例:需要通過(guò)一個(gè)學(xué)生的id 查詢出這個(gè)學(xué)生所在學(xué)校的的名字
根據(jù)SimpleJpaRepository寫一個(gè)Repository。

package spring.demo.security.dao;

import org.apache.commons.beanutils.ConvertUtils;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.transform.Transformers;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author td
 * @date 2017/10/19
 */
@Transactional
@Repository
public class CustomDao {
    @PersistenceContext
    EntityManager entityManager;

    public List<?> queryListEntity (String sql, Map<String,Object> params,Class<?> clazz) {
        Session session = entityManager.unwrap(Session.class);
        SQLQuery sqlQuery = session.createSQLQuery(sql);
        System.out.println(sqlQuery.toString());
        if (params != null) {
            for (String key:params.keySet()) {
                sqlQuery.setParameter(key,params.get(key));
            }
        }
        System.out.println(sqlQuery.toString());
        sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<Map<String,Object>> result = sqlQuery.list();

        if (clazz != null) {
            List<Object> entityList = convert(clazz,result);
            return entityList;
        }
        return result;

    }

    private List<Object> convert(Class<?> clazz, List<Map<String, Object>> list) {
        List<Object> result;
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        result = new ArrayList<Object>();
        try {
            PropertyDescriptor[] props = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
            for (Map<String, Object> map : list) {
                Object obj = clazz.newInstance();
                for (String key:map.keySet()) {
                    String attrName = key.toLowerCase();
                    for (PropertyDescriptor prop : props) {
                        attrName = removeUnderLine(attrName);

                        if (!attrName.equals(prop.getName())) {
                            continue;
                        }

                        Method method = prop.getWriteMethod();
                        Object value = map.get(key);
                        if (value != null) {
                            value = ConvertUtils.convert(value,prop.getPropertyType());
                        }
                        method.invoke(obj,value);
                    }
                }
                result.add(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException("數(shù)據(jù)轉(zhuǎn)換錯(cuò)誤");
        }
        return result;
    }
    // 將下劃線轉(zhuǎn)換為駝峰命名方式
    private String removeUnderLine(String attrName) {
        //去掉數(shù)據(jù)庫(kù)字段的下劃線
        if(attrName.contains("_")) {
            String[] names = attrName.split("_");
            String firstPart = names[0];
            String otherPart = "";
            for (int i = 1; i < names.length; i++) {
                String word = names[i].replaceFirst(names[i].substring(0, 1), names[i].substring(0, 1).toUpperCase());
                otherPart += word;
            }
            attrName = firstPart + otherPart;
        }
        return attrName;
    }

    public Integer getCountBy(String sql,Map<String,Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        if (params != null) {
            for (String key : params.keySet()) {
                query.setParameter(key,params.get(key));
            }
        }
        BigInteger bigInteger = (BigInteger) query.getSingleResult();
        return bigInteger.intValue();
    }
    public Integer deleteOrUpDate(String sql,Map<String,Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        if (params != null) {
            for (String key: params.keySet()) {
                query.setParameter(key,params.get(key));
            }
        }

        return query.executeUpdate();
    }

}

進(jìn)行測(cè)試

   // 查詢一個(gè)id為1的學(xué)生學(xué)校名字
        String sql = "select b.* from student a left join school b on a.school_id=b.id where a.id=:id";
      //  String sql = "select * from person where id = :id";
        Map<String,Object> map = new HashMap<>();
        map.put("id",1);
        List<School> schools = (List<School>) customDao.queryListEntity(sql,map, School.class);
        System.out.println(schools.get(0).getId()+"-"+schools.get(0).getName());

        int count = customDao.getCountBy("select count(*) from student",null);

        System.out.println(count);

關(guān)聯(lián)查詢

舉例:需要通過(guò)學(xué)校的id查詢?cè)搶W(xué)生的所有信息
從以上需求可以看出需要到school和student關(guān)系的主體表中進(jìn)行查詢工育。即在student表中進(jìn)行查詢虾宇,通過(guò)jpa規(guī)范書寫查詢


    //通過(guò)學(xué)生id進(jìn)行查詢
     SysRole findById(Integer id);

     //通過(guò)學(xué)校id進(jìn)行關(guān)聯(lián)查詢角色,關(guān)聯(lián)的用戶表用在主體表中的表示名Student   關(guān)聯(lián)的id后用_Id
      //所以可以看出關(guān)聯(lián)查詢主要是通過(guò)_進(jìn)行的
     List<Student> findBySchool_Id(int id);
     

從例子中可以看出關(guān)聯(lián)查詢主要是用""符號(hào)翅娶,
如果通過(guò)本章表中id查詢本章表中的role信息可以這么寫:findById()
如果通過(guò)本章用戶表中id查詢本章表中的role信息可以這么寫:findBySUser_Id()
如果既要通過(guò)本章表中的id查詢有要通過(guò)用戶表中的id進(jìn)行查詢,可以這么寫:findBySUser_IdAndId()
如何還想關(guān)聯(lián)更多的表可以在后面添加:And+表名字+“
”+表中要查詢的字段文留。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末好唯,一起剝皮案震驚了整個(gè)濱河市竭沫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骑篙,老刑警劉巖蜕提,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異靶端,居然都是意外死亡谎势,警方通過(guò)查閱死者的電腦和手機(jī)凛膏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脏榆,“玉大人猖毫,你說(shuō)我怎么就攤上這事⌒胛梗” “怎么了吁断?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坞生。 經(jīng)常有香客問(wèn)我仔役,道長(zhǎng),這世上最難降的妖魔是什么是己? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任又兵,我火速辦了婚禮,結(jié)果婚禮上卒废,老公的妹妹穿的比我還像新娘沛厨。我一直安慰自己,他們只是感情好升熊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布俄烁。 她就那樣靜靜地躺著,像睡著了一般级野。 火紅的嫁衣襯著肌膚如雪页屠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天蓖柔,我揣著相機(jī)與錄音辰企,去河邊找鬼。 笑死况鸣,一個(gè)胖子當(dāng)著我的面吹牛牢贸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播镐捧,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼潜索,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了懂酱?” 一聲冷哼從身側(cè)響起竹习,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎列牺,沒(méi)想到半個(gè)月后整陌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年泌辫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了随夸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡震放,死狀恐怖宾毒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情殿遂,我是刑警寧澤伍俘,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站勉躺,受9級(jí)特大地震影響癌瘾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饵溅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一妨退、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜕企,春花似錦咬荷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至唇牧,卻和暖如春罕扎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丐重。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工腔召, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扮惦。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓臀蛛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親崖蜜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浊仆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容