SSH初體驗系列--Hibernate--2--crud操做

Ok,今天比較詳細的學習一下hibernate的C(create)、R(read)、U(update)、D(delete) 相關api...html

前言

  Session: 是Hibernate持久化操做的基礎,提供了衆多的數據庫操做方法,如save(),update(),delete()。。。etc,用於完成對象的增長,修改,刪除等方法.java

  後面代碼中使用到的HinernateUtil類:是用於構建SessionFactory(Hibernate提供的獲取session的工廠類)的一個封裝類,在前面的文章SSH初體驗系列--Hibernate--1--環境配置及demo中有詳細代碼,可自行查看.android

一) 增(C)

  1.添加單條個數據;

    @org.junit.Test
    public void save(){
        //建立對象
        Feedback newItem=new Feedback();
        newItem.setUsername("andew17");
        newItem.setContent("測試單條插入");
        newItem.setSendTime(new Timestamp(System.currentTimeMillis()));

        Session session=null;
        Transaction tx=null;
        try{
            //獲取session對象
            session= HibernateUtil.openSession();
            //開啓事務
            tx=session.beginTransaction();
            //執行保存
            session.save(newItem);
            //提交事務
            tx.commit();
        }catch(Exception e){
            //異常時,事務回滾
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            //釋放session
            session.close();
        }
    }
添加單條數據

  2.批量添加數據;

    能夠經過for循環,將數據統一添加到一級緩存session中,再commit到數據庫;sql

@org.junit.Test
    public void saveMany(){
        //建立對象
        List<Feedback> feedbackList=new ArrayList<>();
        for(int i=0;i<10;i++){
            Feedback newItem=new Feedback();
            newItem.setUsername("andew"+i);
            newItem.setContent("測試單條插入"+i);
            newItem.setSendTime(new Timestamp(System.currentTimeMillis()));
            feedbackList.add(newItem);
        }
        Session session=null;
        Transaction tx=null;
        try{
            //獲取session對象
            session= HibernateUtil.openSession();
            //開啓事務
            tx=session.beginTransaction();
            //執行保存,此時只是保存到Session緩存中,並無同步到數據庫
            Feedback feedback=null;
            for(int i=0;i<feedbackList.size();i++){
                feedback=feedbackList.get(i);
                session.save(feedback);
            }
            //提交事務,就緩存同步到數據庫
            tx.commit();
        }catch(Exception e){
            //異常時,事務回滾
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            //釋放session
            session.close();
        }
    }
for循環執行session的save方法

    若是數據量不大時,或者對性能沒有極致的追求時,這種方法還ok;可是當批量插入數據量較大時,這個方式的弊端就體現出來了.數據庫

    弊端主要有兩點:a,循環向Session添加,修改數據時,Session對象自身開闢的一級緩存會不斷被消耗,直到耗盡(outOfMemoryError);編程

             b,經過在hibernate.cfg.xml文件配置"顯示sql語句"api

        <!--顯示SQL語句-->
        <property name="show_sql">true</property>

              再跟蹤打印的SQl語句,很容易就發現,內部其實仍是執行了n(n=數據量)次的插入操做,而不是一次語句執行,在性能上,可想並非很好. 對於喜歡追求代碼性能的小夥伴,這是讓人難以忍受的,如鯁在喉...緩存

Hibernate: select max(id) from feedback
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Disconnected from the target VM, address: '127.0.0.1:59399', transport: 'socket'

Process finished with exit code 0

    解決方案:a,對於內存溢出,能夠在代碼中進行判斷,每隔n條,釋放一次緩存;而且在hibernate.xfg.xml中配置每次提交sql的數量session

<property name="hibernate.jdbc.batch_size">10</property>
// 每處理20條清空緩存
session.save(newItem);
if (i%20 == 0) {
    session.flush();
    session.clear();
}

        b.若想在性能上有所提高,能夠繞過Hibernate Api,直接使用jdbc api來作批量插入;app

@org.junit.Test
    public void saveJdbc(){
        //建立對象
        List<Feedback> feedbackList=new ArrayList<>();
        for(int i=0;i<20;i++){
            Feedback newItem=new Feedback();
            newItem.setUsername("andew"+i);
            newItem.setContent("測試單條插入"+i);
            newItem.setSendTime(new Timestamp(System.currentTimeMillis()));
            feedbackList.add(newItem);
        }
        String insertSql="insert into feedback (username, content, sendTime) values (?, ?, ?)";

        Session session=null;
//        Transaction tx=null;
        try{
            session= HibernateUtil.openSession();
            //tx=session.beginTransaction();
            session.doWork(new Work() {
                @Override
                public void execute(Connection connection) throws SQLException {
                    //這裏面就獲得connection了
                    PreparedStatement stmt=connection.prepareStatement(insertSql);
                    //方式1:自動提交
                    /*connection.setAutoCommit(true);
                    for(int i=0;i<feedbackList.size();i++){
                        stmt.setString(1,"andrew"+1);
                        stmt.setString(2,"test content "+i);
                        stmt.setTimestamp(3,new Timestamp(System.currentTimeMillis()));
                        stmt.execute();//此語句,每次執行,都會將一條數據插入到db
                    }*/

                    //方式2:批量提交
                    connection.setAutoCommit(false);
                    for(int i = 0; i<feedbackList.size();i++) {
                        stmt.setString(1,"andrew"+1);
                        stmt.setString(2,"test content "+i);
                        stmt.setTimestamp(3,new Timestamp(System.currentTimeMillis()));
                        stmt.addBatch();
                        if (i % 10 == 0) {
                            stmt.executeBatch();
                            connection.commit();//此處執行一次db插入操做
                        }
                    }
                    stmt.executeBatch();
                    connection.commit();
                }
            });
            ////注意:此時不能再用事務,會報錯:org.hibernate.TransactionException: Unable to commit against JDBC Connection
            // tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            //釋放session
            session.close();
        }
    }
jdbc批量插入數據

    其中有幾個地方須要注意:

      a,connection對象的獲取:

a)過期方式:session.connection();
     可是這個方法Hibernate不推薦使用,The method connection() from the type         Session is deprecated
    在3.3之後的版本中已經被廢除了。

b)Session.doWork;
    3.3官方的替代方法是用Session.doWork(Work work);
    傳入的參數work是一個接口,能夠:
    HibernateFactory.getSession().doWork(
          new Work() {
            public void execute(Connection connection) {
              // 這裏面就獲得connection了,    
                }
          }
        );   

      b.代碼中的"方式2,批量提交",就是咱們最終想要的高性能的答案;

  3.使用一對一關聯添加數據

    在實際開發中,咱們常常會遇到表關聯的狀況,好比一張數據表的記錄與另外一張數據表記錄一一對應,即:一一對應關係,此時咱們對錶數據的添加最好也應該是一次性關聯添加的.Hibernate就提供了相關的方法,供開發人員處理這種關係的映射,下面就來學習一下.

    此處,咱們使用兩張新表來進行學習,UserInfo和UserExtend,其中UserInfo記錄用戶的姓名,性別,出生日期等基礎信息,UserExtend記錄擴充信息,如職業,公司,地址等。UserInfo中經過一個UserExtend屬性與UserExtend表相關聯。具體以下:

/**
 * Created by c-yangx on 11/18/2016.
        */
public class UserInfo {
    private int id;
    private String name;
    private Timestamp birthday;
    private String sex;
    private UserExtend userExtend;

    public int getId() {return id;}

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

    public String getName() {return name;}

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

    public Timestamp getBirthday() {return birthday;}

    public void setBirthday(Timestamp birthday) {this.birthday = birthday;}

    public String getSex() {return sex;}

    public void setSex(String sex) {this.sex = sex;}

    public UserExtend getUserExtend() {return userExtend;}

    public void setUserExtend(UserExtend userExtend) {this.userExtend = userExtend;}
}
UserInfo.java
/**
 * Created by c-yangx on 11/18/2016.
 */
public class UserExtend {
    private int id;
    private String position;
    private String company;
    private String address;
    private UserInfo userInfo;

    public int getId() {return id;}
    public void setId(int id) {this.id = id;}

    public String getPosition() {return position;}

    public void setPosition(String position) {this.position = position;}

    public String getCompany() {return company;}

    public void setCompany(String company) {this.company = company;}

    public String getAddress() {return address;}

    public void setAddress(String address) {this.address = address;}

    public UserInfo getUserInfo() {return userInfo;}

    public void setUserInfo(UserInfo userInfo) {this.userInfo = userInfo;}
}
UserExtend.java
<?xml version="1.0"  encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="models">
    <class name="models.UserInfo" table="userinfo" catalog="public">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="native"></generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="45" not-null="true">
                <comment>姓名</comment>
            </column>
        </property>
        <property name="birthday" type="java.sql.Timestamp">
            <column name="birthday" length="20" not-null="true">
                <comment>出生日期</comment>
            </column>
        </property>
        <property name="sex" type="java.lang.String">
            <column name="sex" length="5" not-null="true">
                <comment>性別:w=女;m=男;</comment>
            </column>
        </property>
        <many-to-one name="userExtend" column="userExtend" unique="true" not-null="true" cascade="all"></many-to-one>
    </class>
</hibernate-mapping>
UserInfo.hbm.xml
<?xml version="1.0"  encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="models">
    <class name="models.UserExtend" table="userextend" catalog="public">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="native"></generator>
        </id>
        <property name="position" type="java.lang.String">
            <column name="position" length="50" not-null="false">
                <comment>職位</comment>
            </column>
        </property>
        <property name="company" type="java.lang.String">
            <column name="company" length="200" not-null="false">
                <comment>公司名</comment>
            </column>
        </property>
        <property name="address" type="java.lang.String">
            <column name="address" length="50" not-null="false">
                <comment>住址</comment>
            </column>
        </property>
        <one-to-one name="userInfo" property-ref="userExtend"></one-to-one>
    </class>
</hibernate-mapping>
UserExtend.hbm.xml

    測試代碼爲:

@org.junit.Test
    public void save_one2one() throws ParseException {
        //建立對象
        UserInfo userInfo=new UserInfo();
        UserExtend userExtend=new UserExtend();
        userExtend.setPosition("IT");
        userExtend.setCompany("guess");
        userExtend.setAddress("BeiJing CangPing");
        userInfo.setName("andrew");

        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
        userInfo.setBirthday(new Timestamp(simpleDateFormat.parse("1992-01-10").getTime()));

        userInfo.setSex("m");
        userInfo.setUserExtend(userExtend);

        Session session=null;
        Transaction tx=null;
        try{
            session= HibernateUtil.openSession();
            tx=session.beginTransaction();

            session.save(userInfo);

            tx.commit();
        }catch(Exception e){
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            session.close();
        }
    }

    從細分類上,此種屬於"惟一外鍵雙向關聯",其餘的還有"多對一單向關聯"、"多對多單向關聯"等,這個話題內容較多,就不在此處展開,後面也許會單開一篇文章研究一下.

    對了,最後必定別忘了在主配置文件hibernate.cfg.xml中添加mapping配置。

    放一張數據庫截圖,生成表結構如圖所示:

  4.一對多關聯添加數據.

    還有一種開發中常見的需求是,表的一對多關心,舉個生活中的例子:一個支付寶帳號綁定多張銀行卡,每張卡都會顯示他本身是哪一個銀行的,卡號是多少...等信息;

    那麼咱們這裏就用兩張新表來演示,一張帳戶表(Account),類比於支付寶帳戶;一張綁定銀行卡表(BindCard),記錄例子中的綁定銀行卡信息;一個帳戶會對應有多條綁定銀行卡記錄.

    a> 兩個model類的代碼以下:

/**
 * Created by c-yangx on 11/18/2016.
 */
public class Account {

    private int id;
    private String name;
    private Set<BindCard> bindCards;

    public int getId() {return id;}

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

    public String getName() {return name;}

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

    public Set<BindCard> getBindCards() {return bindCards;}

    public void setBindCards(Set<BindCard> bindCards) {this.bindCards = bindCards;}
}
Account.java
/**
 * Created by c-yangx on 11/18/2016.
 */
public class BindCard {
    private int id;
    private String cardNum;
    private int cardType;

    private Account account;

    public int getId() {return id;}

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

    public String getCardNum() {return cardNum;}

    public void setCardNum(String cardNum) {this.cardNum = cardNum;}

    public int getCardType() {return cardType;}

    public void setCardType(int cardType) {this.cardType = cardType;}

    public Account getAccount() {return account;}

    public void setAccount(Account account) {this.account = account;}
}
BindCard.java

    b>Account.hbm.xml;

      其中有幾點須要特別說明一下:

        1.在映射文件裏,經過<set>標籤配置一對多的關係映射;

        2.cascade屬性設置的是級聯操做類型,設置爲all時,是因此操做都能執行;  若是隻進行"建立"和"修改",也可設置成save-update;

        3.<set>標籤的name屬性值=持久化類對應的屬性名;   <key>的column屬性值=與其關聯的表的外鍵;                    

<?xml version="1.0"  encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="models">
    <class name="models.Account" table="account" catalog="public">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="native"></generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="45" not-null="true">
                <comment>帳戶名</comment>
            </column>
        </property>
        <set name="bindCards" cascade="all" lazy="false">
            <key column="cardId"/>
            <one-to-many class="models.BindCard"/>
        </set>
    </class>
</hibernate-mapping>

    c>最後是測試代碼,這就沒啥好多說的了

@org.junit.Test
    public void save_one2many(){
        BindCard bindCard1=new BindCard();
        bindCard1.setCardNum("1234343242");
        bindCard1.setCardType(0);

        BindCard bindCard2=new BindCard();
        bindCard2.setCardNum("3421213131");
        bindCard2.setCardType(1);

        Set<BindCard> set=new HashSet<>();
        set.add(bindCard1);
        set.add(bindCard2);

        Account account=new Account();
        account.setName("andrew's account");
        account.setBindCards(set);

        Session session=null;
        Transaction tx=null;
        try{
            session= HibernateUtil.openSession();
            tx=session.beginTransaction();

            session.save(account);

            tx.commit();
        }catch(Exception e){
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            session.close();
        }
    }

      生成的數據表結構:

 

二) 查(R)

  方式一:根據主鍵id,查詢單條記錄,這也是最簡單的方式;

        Session session=HibernateUtil.openSession();
        Feedback feedback= session.get(Feedback.class,60);

  方式二:HQL(hibernate query language)查詢;

     1.HQL查詢語言是什麼? HQL查詢語言是面向對象查詢語言,支持多態查詢;  當咱們想在查詢時,作一些複雜的邏輯,好比where 篩選,order by 排序等,HQL就頂上用了;

        Session session= HibernateUtil.openSession();
        String sqlStr="from Feedback";
        Query q=session.createQuery(sqlStr);
        List<Feedback> feedbacks=q.list();

      作條件篩選時,修改下sqlStr就行:

public List<Feedback> getAll(String userName) {
    Session session = null;
    try {
        session = HibernateUtils.getSession();
        Query q =session.createQuery("from Feedback where userName=?");
        // 注意:參數索引從0開始
        q.setParameter(0, userName);
        // 執行查詢
        return q.list();
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        session.close();
    }
}

   方式三:QBC(Query By Criteria)查詢;

      1)Hibernate 5.2版本以前(暫時尚未覈實是5.2以前的哪個版本開始),

        使用QBC查詢,一般有3個步驟:

          1.用Session實例的createCriterria()方法建立Criteria對象c;

          2.使用Restrictions工具類的相關方法爲建立的對象c設置查詢條件。

            常見的方法有: a) Restrictions.eq = 等於;

                    b) Restrictions.allEq = 使用Map、key/value進行多個等於的對比;

                    c) Restrictions.gt = 大於;

                    d) Restrictions.ge = 大於等於;

                    e) Restrictions.ct = 小於;

                    f)  Restrictions.le = 小於等於;

                    g)  Restrictions.between = 對應SQL的between子句;

                    h) Restrictions.like = 對應SQL的like 子句;

                    i) Restrictions.in = 對應SQL 的in子句;

          3.使用對象c的list()方法執行查詢,獲取結果;

       2) Hibernate 5.2版本時,createCriteria()方法已通過時了,新版中使用的是鏈式的查詢語句(相似C#中的linq to sql,還有如今android中的rxjava,看來,鏈式編程以其優美的代碼風格,便於維護的特性,愈來愈成爲潮流)

        下面放一段核心代碼 (可能由於h5.2版本較新,關於createCriteria()替代方法的查詢結果,在百度上暫時很難找到,這個地方也是費了老大勁了. ):

@org.junit.Test
    public void get_qbc(){
        Session session= HibernateUtil.openSession();
        //在Hibernate 5.2中,此方法過期了
        //Criteria criteria=session.createCriteria(Feedback.class);

        //用新的替代方法
        CriteriaBuilder cBuilder=session.getCriteriaBuilder();
        CriteriaQuery<Feedback> cQuery= cBuilder.createQuery(Feedback.class);
        Root<Feedback> feedbackRoot=cQuery.from(Feedback.class);


        //1.select all records
        CriteriaQuery<Feedback> select = cQuery.select(feedbackRoot);
        TypedQuery<Feedback> typedQuery = session.createQuery(select);
        List<Feedback> resultlist = typedQuery.getResultList();
        /*
        最終打印的sql語句:
        Hibernate: select feedback0_.id as id1_2_, feedback0_.username as username2_2_, feedback0_.content as content3_2_, feedback0_.sendTime as sendTime4_2_ 
            from feedback feedback0_
        **/

        //2.Ordering the records
        CriteriaQuery<Feedback> select1 = cQuery.select(feedbackRoot);
        select1.orderBy(cBuilder.asc(feedbackRoot.get("id")));
        TypedQuery<Feedback> typedQuery1 = session.createQuery(select1);
        List<Feedback> resultlist1 = typedQuery1.getResultList();
        /*
        最終打印的sql語句:
        Hibernate: select feedback0_.id as id1_2_, feedback0_.username as username2_2_, feedback0_.content as content3_2_, feedback0_.sendTime as sendTime4_2_ 
            from feedback feedback0_ order by feedback0_.id asc
        * */


        //3.where select條件篩選查詢
        CriteriaQuery<Feedback> select2 = cQuery.select(feedbackRoot);
        select2.orderBy(cBuilder.asc(feedbackRoot.get("id")));
        select2.where(cBuilder.ge(feedbackRoot.get("id"),60));
        TypedQuery<Feedback> typedQuery2 = session.createQuery(select2);
        List<Feedback> resultlist2 = typedQuery2.getResultList();
        /*
        最終打印的sql語句:
        Hibernate: select feedback0_.id as id1_2_, feedback0_.username as username2_2_, feedback0_.content as content3_2_, feedback0_.sendTime as sendTime4_2_ 
            from feedback feedback0_ where feedback0_.id>=60 order by feedback0_.id asc
        * */


        System.out.println("result ok");
    }

 

三) 改(U)、刪(D)

  相較於"CR",修改和刪除的需求相對就比較簡單了,沒有那麼多的複雜需求,這個地方就簡單帶過了;

        //修改(updateInstance=要修改的實例)
        session.update(updateInstance);
        //刪除(deleteInstance=要刪除的實例)
        session.delete(deleteInstance);

 

後言

  呼~~總算能鬆口氣了。。。

  真正寫的時候,才發現, "crud",叫起來多麼簡單的一個單詞,中間的文章何其多,深挖起來,幾天幾夜也說不完; hibernate一個流行的框架,裏面的內容又是何其多,博主實在是不敢繼續繼續深刻下去了,不然這篇文章發表不知要到何年何月去了(哈哈~~開個玩笑)...

  文章內容雖然不深,不過博主是在用心寫,裏面的每一段代碼,都是測試運行,確認無誤的...(本身受夠了網上前言不搭後語的demo代碼,罵過... 一樣緣由,可不想本身也被參考了文內代碼的人在背後罵^_^)若是你們發現什麼問題,也歡迎評論,有問題的地方,我會及時修正;

  文章寫着挺耗時的,不過樓主會堅持下去。理由只有兩個:1.若是你也剛開始學Hibernate,本文能讓你以爲多少有些收穫,我就會以爲本身的沒有作無用功;  2.博主本身忘了的時候,業能夠回頭來翻看;

  也歡迎你們關注,共同窗習,共同進步.

 本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利;

本文出自:博客園--別問是誰

相關文章
相關標籤/搜索