jpa2.x的getOne()/findOne()/findById()的區別及使用


使用spring-cloud 2.0.3時,加入了spring-boot-start-data-jpa依賴之后並配置完成,然后進行測試:

這是service 方法,注入了IUserInfoDao接口(繼承JpaRepository<UserInfoModel, Integer>);

    @Override
    public UserInfoModel getById(Integer id) {
        return userInfoDao.getOne(id);
    }
這是測試方法,注入IUserInfoService接口;

在測試中先使用的是getOne() 的方法,僅做查詢可以得到對象信息,

    /**
     * 測試查詢指定用戶信息
     */
    @Test
    public void getById(){
        UserInfoModel getResult = userInfoService.getById(1);
        System.err.println("id : " + getResult.getUserId());
    }


但是在測試編輯(update)對象時,執行測試時曝出異常:org.hibernate.LazyInitializationException: could not initialize proxy - no Session的異常;

    @Test
    public void updateUserInfo(){
        UserInfoModel oldUser = userInfoService.getById(1);
        oldUser.setUserAddress("中國陝西");
        UserInfoModel updateResult = userInfoService.update(oldUser);
        System.err.println("email : " + updateResult.getUserEmail());
    }
異常信息如下:



查了好多資料說,在方法上加入@Transactional可以解決問題懶加載的問題,但是在JUnit測試update時,需要先查詢,在設置數據進行更新,然而@Transactional會回滾,導致數據更新無效

    /**
     * 測試修改用戶信息
     */
    @Test
    @Transactional
    public void updateUserInfo(){
        UserInfoModel oldUser = userInfoService.getById(1);
        oldUser.setUserAddress("中國陝西");
        UserInfoModel updateResult = userInfoService.update(oldUser);
        System.err.println("email : " + updateResult.getUserEmail());
    }
看打印的信息,是更新成功了,但是數據庫並沒有變



仔細看下面有一條信息,Rolled back ...



紅色部分全部信息如下:



后來查看api 發現getOne()方法返回的是實體對象的代理對象(a reference),源碼如下:

    /**
     * Returns a reference to the entity with the given identifier.
     *
     * @param id must not be {@literal null}.
     * @return a reference to the entity with the given identifier.
     * @see EntityManager#getReference(Class, Object)
     * @throws javax.persistence.EntityNotFoundException if no entity exists for given {@code id}.
     */
    T getOne(ID id);
在CrudRepository<T, ID>接口中有一個findById()的方法 ,源碼如下:

    /**
     * Retrieves an entity by its id.
     *
     * @param id must not be {@literal null}.
     * @return the entity with the given id or {@literal Optional#empty()} if none found
     * @throws IllegalArgumentException if {@code id} is {@literal null}.
     */
    Optional<T> findById(ID id);
該方法的返回值是一個Optional<T>,在Optional類中有個get()方法,返回的是當前對象/值,源碼如下:

    /**
     * If a value is present in this {@code Optional}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the non-null value held by this {@code Optional}
     * @throws NoSuchElementException if there is no value present
     *
     * @see Optional#isPresent()
     */
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
通過看源碼和api,發現可以使用findById(),先調用findById()返回封裝后的對象,然后使用get()方法,返回實體對象。

去掉update()方法上的@Transactional注解,將getOne()換成findById()

    @Override
    public UserInfoModel getById(Integer id) {
        //使用getOne()返回的是代理對象,無法直接操作,會出現hibernate lazyxxx  no session 的錯誤
        //在測試方法上加入@Transactional注解可以解決報錯的問題
//        return userInfoDao.getOne(id);
        Optional<UserInfoModel> findResult = userInfoDao.findById(id);
        return findResult.get();
    }
在進行編輯測試,數據庫信息更新成功。





在QueryByExampleExecutor<T>接口中有一個findOne()方法,源碼如下:

    /**
     * Returns a single entity matching the given {@link Example} or {@literal null} if none was found.
     *
     * @param example must not be {@literal null}.
     * @return a single entity matching the given {@link Example} or {@link Optional#empty()} if none was found.
     * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the Example yields more than one result.
     */
    <S extends T> Optional<S> findOne(Example<S> example);
 對於這個Example<S>,其實是一個查詢條件的封裝實例,比如要條件查詢UserInfo的信息(userNameCn="xxx"),則通過Example.of(userInfo)方法創建Example<UserInfoModel> 的一個對象,然后調用findOne()方法;

     @Override
    public UserInfoModel findOne(UserInfoModel userInfo) {
        //Example對象可以當做查詢條件處理,將查詢條件得參數對應的屬性進行設置即可
        //可以通過ExampleMatcher.matching()方法進行進一步得處理
        Example<UserInfoModel> userExample = Example.of(userInfo);
        Optional<UserInfoModel> exampleResult = userInfoDao.findOne(userExample);
        //需要結果過做判斷,查詢結果為null時會報NoSuchElementException
        if (exampleResult.isPresent()) {
            return exampleResult.get();
        }
        return null;
    }
findOne()方法會返回一個Optional<T>對象,再Optional類中有很多內置的方法,其中isPresen()方法返回Optional對象是否為null的結果,如果當前Optional對象有值,則返回true,否則返回false,當結果有值時,然后調用它的get()方法,會返回一個<T>類型的對象,即我們要查詢的實例結果。

isPresent()源碼如下:

    /**
     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
    public boolean isPresent() {
        return value != null;
    }
get()源碼如下:

    /**
     * If a value is present in this {@code Optional}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the non-null value held by this {@code Optional}
     * @throws NoSuchElementException if there is no value present
     *
     * @see Optional#isPresent()
     */
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }


免責聲明!

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



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