使用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;
}