JPA註解詳解及示例
文章出處:https://blog.csdn.net/zhengchao1991/article/details/70236919
1、用例項介紹JPA
使用JPA,肯定是有ORM的意思在裡面,當然我們首先要定義一個實體類(要將 Java 類指定為 JPA 實體,請使用 @Entity 批註):
-
package com.creditcloud.lending.entities;
-
import com.creditcloud.common.entities.UUIDEntity;
-
import com.creditcloud.common.entities.embedded.Duration;
-
import com.creditcloud.model.constant.LoanConstant;
-
import com.creditcloud.model.constraints.IncrementalInteger;
-
import com.creditcloud.model.enums.loan.LoanSettleStatus;
-
import com.creditcloud.model.enums.loan.LoanStatus;
-
import com.creditcloud.model.enums.loan.RepaymentMethod;
-
import java.util.Date;
-
import javax.persistence.Column;
-
import javax.persistence.Entity;
-
import javax.persistence.EnumType;
-
import javax.persistence.Enumerated;
-
import javax.persistence.JoinColumn;
-
import javax.persistence.ManyToOne;
-
import javax.persistence.NamedQueries;
-
import javax.persistence.NamedQuery;
-
import javax.persistence.Table;
-
import javax.persistence.Temporal;
-
import javax.persistence.TemporalType;
-
import javax.persistence.UniqueConstraint;
-
import javax.validation.constraints.Min;
-
import javax.validation.groups.Default;
-
import lombok.Data;
-
import lombok.NoArgsConstructor;
-
@Data
-
@NoArgsConstructor
-
@Entity
-
@Table(name = "TB_LOAN",
-
uniqueConstraints
-
= @UniqueConstraint(columnNames = {"REQUEST_ID, ORDINAL"}))
-
@NamedQueries({
-
@NamedQuery(name = "Loan.listByStatusAndDate",
-
query = "select l from Loan l where l.status in :statusList and l.timeOpen BETWEEN :from and :to order by l.timeOpen DESC"),
-
@NamedQuery(name = "Loan.markRewarded",
-
query = "update Loan l set l.rewarded = :rewarded where l.id in :ids")
-
})
-
public class Loan extends UUIDEntity {
-
@ManyToOne
-
@JoinColumn(name = "REQUEST_ID",
-
nullable = false)
-
private LoanRequest loanRequest;
-
/**
-
* 貸款標題預設是LoanRequest的標題,可以更改
-
*/
-
@Column(nullable = true)
-
private String title;
-
/**
-
* 還款方式可以和原始的LoanRequest不同,拆標時可能會出現這個情況
-
*/
-
@Enumerated(EnumType.STRING)
-
@Column(name = "METHOD", nullable = false)
-
private RepaymentMethod method;
-
/**
-
* 序號,同一LoanRequest拆分到多個Loan時用以區分 從 1 開始
-
*/
-
@Min(1)
-
@Column(nullable = false)
-
private int ordinal;
-
/**
-
* 金額
-
*/
-
@Column(nullable = false)
-
@IncrementalInteger(min = LoanConstant.MIN_LOAN_AMOUNT,
-
increment = LoanConstant.LOAN_AMOUNT_INCREMENT,
-
max = LoanConstant.MAX_LOAN_AMOUNT,
-
groups = Default.class)
-
private int amount;
-
/**
-
* 年化收益率,單位 1/10000
-
*/
-
@Column(nullable = false)
-
@IncrementalInteger(min = LoanConstant.MIN_LOAN_RATE,
-
increment = 1,
-
max = LoanConstant.MAX_LOAN_RATE)
-
private int rate;
-
/**
-
* 期限
-
*/
-
@Column(nullable = false)
-
private Duration duration;
-
@Enumerated(EnumType.STRING)
-
@Column(nullable = false)
-
private LoanStatus status;
-
/**
-
* 用於結標稽核:結標需要二次稽核
-
*/
-
@Enumerated(EnumType.STRING)
-
//@Column(nullable = false)
-
private LoanSettleStatus settleStatus;
-
/**
-
* 開放募集時間,單位:小時 timeout = 24 就是募集期為1天 最長3天
-
*/
-
@IncrementalInteger(min = LoanConstant.MIN_LOAN_TIME_OUT,
-
increment = 1,
-
max = LoanConstant.MAX_LOAN_TIME_OUT,
-
groups = Default.class)
-
@Column(nullable = true)
-
private int timeOut;
-
/**
-
* 開始募集時間
-
*/
-
@Temporal(TemporalType.TIMESTAMP)
-
@Column(nullable = true)
-
private Date timeOpen;
-
/**
-
* 募整合功結束時間
-
*/
-
@Temporal(TemporalType.TIMESTAMP)
-
@Column(nullable = true)
-
private Date timeFinished;
-
/**
-
* 結標時間,資金結算時間
-
*/
-
@Temporal(TemporalType.TIMESTAMP)
-
@Column(nullable = true)
-
private Date timeSettled;
-
/**
-
* 還清時間
-
*/
-
@Temporal(TemporalType.TIMESTAMP)
-
@Column(nullable = true)
-
private Date timeCleared;
-
@Column(nullable = false)
-
private boolean mortgaged;
-
/**
-
* 貸款的投標數
-
*/
-
@Min(0)
-
@Column(name = "BID_NUMBER", nullable = true)
-
private int bidNumber;
-
/**
-
* 實際投標金額,主要用於流標時記錄實際投資額
-
*/
-
@Min(0)
-
@Column(name = "BID_AMOUNT", nullable = true)
-
private int bidAmount;
-
/**
-
* 此標是否已經獎勵過
-
*/
-
private boolean rewarded;
-
/**
-
* 是否是流標後結算自動拆標生成的,同用戶手動拆標區分開
-
*/
-
private boolean autoSplitted;
-
/**
-
* 結標稽核-風控通過 : 稽核意見
-
*/
-
private String riskComments;
-
/**
-
* 結標稽核通過 : 稽核意見
-
*/
-
private String comments;
-
/**
-
* 結標稽核-風控 拒絕 : 拒絕原因
-
*/
-
private String riskRejectComments;
-
/**
-
* 結標稽核 拒絕 : 拒絕原因
-
*/
-
private String rejectComments;
-
public Loan(LoanRequest loanRequest,
-
String title,
-
RepaymentMethod method,
-
int ordinal,
-
int amount,
-
int rate,
-
Duration duration,
-
LoanStatus status,
-
int timeOut,
-
Date timeOpen,
-
Date timeFinished,
-
Date timeSettled,
-
boolean mortgaged,
-
int bidNumber,
-
int bidAmount) {
-
this.loanRequest = loanRequest;
-
this.title = title;
-
this.method = method;
-
this.ordinal = ordinal;
-
this.amount = amount;
-
this.rate = rate;
-
this.duration = duration;
-
this.status = status;
-
this.timeOut = timeOut;
-
this.timeOpen = timeOpen;
-
this.timeFinished = timeFinished;
-
this.timeSettled = timeSettled;
-
this.bidNumber = bidNumber;
-
this.bidAmount = bidAmount;
-
this.mortgaged = mortgaged;
-
}
-
}
@Entity註解:標記該bean是一個import javax.persistence.Entity;,即一個與資料庫表對映的bean的意思。
@Table註解:指定該bean對應的資料庫表,name屬性指定了資料庫的表名為:TB_LOAN。
@NamedQueries註解:用於編寫JPQL語法的sql語句。上面的例子中,我們給了一個查詢、一個修改的語句。
uniqueConstraints = @UniqueConstraint(columnNames = {"REQUEST_ID, ORDINAL"}):定義唯一約束,或者聯合約束。
@NoArgsConstructor註解:給該實體類宣告一個無引數的建構函式,對應有一個全部參賽的建構函式註解:@AllArgsConstructor。
看下他的父類:
-
package com.creditcloud.common.entities;
-
import com.creditcloud.model.constant.EntityConstant;
-
import javax.persistence.Column;
-
import javax.persistence.GeneratedValue;
-
import javax.persistence.Id;
-
import javax.persistence.MappedSuperclass;
-
import org.eclipse.persistence.annotations.UuidGenerator;
-
@MappedSuperclass
-
public abstract class UUIDEntity extends BaseEntity {
-
@UuidGenerator(name = "UUID_GEN")
-
@Id
-
@GeneratedValue(generator = "UUID_GEN")
-
@Column(name = "ID", length = EntityConstant.UUID_LENGTH)
-
private String id;
-
public String getId() {
-
return id;
-
}
-
public void setId(String id) {
-
this.id = id;
-
}
-
}
@UuidGenerator註解:為我們自動生成資料庫記錄的id。
@Id:將此屬性設定為表記錄的id.
而該類繼承了BaseEntity這個抽象類:
-
package com.creditcloud.common.entities;
-
import java.io.Serializable;
-
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
-
import org.apache.commons.lang3.builder.ToStringStyle;
-
public abstract class BaseEntity implements Serializable {
-
static final long serialVersionUID = 20130514L;
-
@Override
-
public String toString() {
-
return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE, false);
-
}
-
}
該類的主要作用是實現Serializable介面,畢竟是基於ejb的專案,肯定是涉及到序列化的,寫的抽象類,讓其他的類都繼承這個抽象類,便有了序列化的功能了。實體類編寫好了,我們開始使用,編寫DAO層,用於操作實體類:
-
package com.creditcloud.lending.entities.dao;
-
import com.creditcloud.common.entities.dao.AbstractDAO;
-
import com.creditcloud.lending.entities.Loan;
-
import com.creditcloud.lending.local.ApplicationBean;
-
import com.creditcloud.model.criteria.PageInfo;
-
import com.creditcloud.model.enums.loan.LoanStatus;
-
import java.util.Arrays;
-
import java.util.Collections;
-
import java.util.Date;
-
import java.util.List;
-
import javax.ejb.EJB;
-
import javax.ejb.LocalBean;
-
import javax.ejb.Stateless;
-
import javax.inject.Inject;
-
import javax.persistence.EntityManager;
-
import javax.persistence.PersistenceContext;
-
import org.slf4j.Logger;
-
@Stateless
-
@LocalBean
-
public class LoanDAO extends AbstractDAO<Loan> {
-
@Inject
-
Logger logger;
-
@EJB
-
ApplicationBean appBean;
-
@PersistenceContext(unitName = "LendingPU")
-
private EntityManager em;
-
public LoanDAO() {
-
super(Loan.class);
-
}
-
@Override
-
protected EntityManager getEntityManager() {
-
return em;
-
}
-
/**
-
* 根據時間篩選標的
-
*
-
* @param statusList
-
* @param pageInfo
-
* @param hiddenList
-
* @return
-
*/
-
public List<Loan> listByStatusAndDate(PageInfo pageInfo, Date from, Date to, LoanStatus... statusList) {
-
if (statusList == null || statusList.length == 0) {
-
return Collections.EMPTY_LIST;
-
}
-
//get results
-
List<Loan> loans = getEntityManager().createNamedQuery("Loan.listByStatusAndDate", Loan.class)
-
.setParameter("from", from)
-
.setParameter("to", to)
-
.setParameter("statusList", Arrays.asList(statusList))
-
.setFirstResult(pageInfo.getOffset())
-
.setMaxResults(pageInfo.getSize())
-
.getResultList();
-
return loans;
-
}
-
/**
-
* update loan rewarded
-
*
-
* @param rewarded
-
* @param ids
-
* @return
-
*/
-
public boolean markRewarded(boolean rewarded, String... ids) {
-
if (ids == null || ids.length == 0) {
-
return false;
-
}
-
int result = getEntityManager()
-
.createNamedQuery("Loan.markRewarded")
-
.setParameter("rewarded", rewarded)
-
.setParameter("ids", Arrays.asList(ids))
-
.executeUpdate();
-
if (appBean.isEnableManualFlush()) {
-
getEntityManager().flush();
-
}
-
return result > 0;
-
}
-
}
寫得兩個方法,用於呼叫我們在實體類中定義的兩個sql語句。
我們都是通過:import javax.persistence.EntityManager;物件來進行操作的,這裡面我們用的是它的createNamedQuery方法進行操作的,即呼叫編寫好的NamedQuery(詳情在實體類定義的)執行;通過setParameter設定查詢等的引數。除了createNamedQuery方法,我們還可以用它的createNativeQuery方法,其實就是原生的sql語句,舉例如下:
-
/**
-
* 根據所屬人列出獎券包
-
*
-
* @param pageInfo
-
* @param owner
-
* @param remainCount 0 未發完 | -1 全部
-
* @return
-
*/
-
public PagedResult<CouponBucket> listByOwnerAndOrder(PageInfo pageInfo, RealmEntity owner, int remainCount) {
-
List rows = getEntityManager()
-
.createNativeQuery("select id, name, amount, displayValue, totalCount, achievedCount, type, productKey, loanId, owner_id, owner_realm, source_id, source_realm, minimumInvest, minimumDuration, maximumDuration, status, timeExpire, timeCreated, description from TB_COUPON_BUCKET where owner_id = ? and (totalCount - achievedCount) > ? order by field(status,'INITIATED','ACHIEVE_UP','EXPIRED','CANCELLED') ASC, timeCreated desc ")
-
.setParameter(1, owner.getEntityId())
-
.setParameter(2, remainCount)
-
.setFirstResult(pageInfo.getOffset())
-
.setMaxResults(pageInfo.getSize())
-
.getResultList();
-
List<CouponBucket> results = new ArrayList<CouponBucket>();
-
if (null != rows && !rows.isEmpty()) {
-
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
for (Object row : rows) {
-
CouponBucket couponBucket = new CouponBucket();
-
Object[] cells = (Object[]) row;
-
couponBucket.setId(cells[0].toString());
-
couponBucket.setName(null == cells[1] || "".equals(cells[1].toString()) ? "" : cells[1].toString());
-
couponBucket.setAmount(null == cells[2] || "".equals(cells[2].toString()) ? null : new BigDecimal(cells[2].toString()));
-
couponBucket.setDisplayValue(null == cells[3] || "".equals(cells[3].toString()) ? "" : cells[3].toString());
-
couponBucket.setTotalCount(null == cells[4] || "".equals(cells[4].toString()) ? 0 : Integer.parseInt(cells[4].toString()));
-
couponBucket.setAchievedCount(null == cells[5] || "".equals(cells[5].toString()) ? 0 : Integer.parseInt(cells[5].toString()));
-
couponBucket.setType(null == cells[6] || "".equals(cells[6].toString()) ? null : CouponBucketType.valueOf(cells[6].toString()));
-
couponBucket.setProductKey(null == cells[7] || "".equals(cells[7].toString()) ? "" : cells[7].toString());
-
couponBucket.setLoanId(null == cells[8] || "".equals(cells[8].toString()) ? "" : cells[8].toString());
-
couponBucket.setOwner(null == cells[9] || null == cells[10] || "".equals(cells[9].toString()) || "".equals(cells[10].toString()) ? null : new RealmEntity(Realm.valueOf(cells[10].toString()), cells[9].toString()));
-
couponBucket.setSource(null == cells[11] || null == cells[12] || "".equals(cells[11].toString()) || "".equals(cells[12].toString()) ? null : new RealmEntity(Realm.valueOf(cells[12].toString()), cells[11].toString()));
-
couponBucket.setMinimumInvest(null == cells[13] || "".equals(cells[13].toString()) ? 0 : Integer.parseInt(cells[13].toString()));
-
couponBucket.setMinimumDuration(null == cells[14] || "".equals(cells[14].toString()) ? 0 : Integer.parseInt(cells[14].toString()));
-
couponBucket.setMaximumDuration(null == cells[15] || "".equals(cells[15].toString()) ? 0 : Integer.parseInt(cells[15].toString()));
-
couponBucket.setStatus(null == cells[16] || "".equals(cells[16].toString()) ? null : CouponStatus.valueOf(cells[16].toString()));
-
try {
-
couponBucket.setTimeExpire(null == cells[17] || "".equals(cells[17].toString()) ? null : sdf.parse(cells[17].toString()));
-
couponBucket.setTimeCreated(null == cells[18] || "".equals(cells[18].toString()) ? null : sdf.parse(cells[18].toString()));
-
} catch (ParseException e) {
-
logger.error("parse time exception CouponBucket failed for [owner={}] [source={}]", owner);
-
}
-
couponBucket.setDescription(null == cells[19] || "".equals(cells[19].toString()) ? "" : cells[19].toString());
-
results.add(couponBucket);
-
}
-
}
-
return new PagedResult<>(results, countByOwner(owner, remainCount));
-
}
大家感覺一下這種寫法,查詢出來的list,我們需要遍歷並構造我們需要的實體類,實在是麻煩,但是這種寫法肯定有它的長處,我們分析如下:
看下之前LoanDAO裡面引入EntityManager的程式碼:
-
@PersistenceContext(unitName = "LendingPU")
-
private EntityManager em;
裡面有個unitName=LendingPU,說明操作Loan物件的EntityManager屬於LendingPU這個Persistence Unit,如果還有一個User物件以及UserDAO,定義如下:
-
@PersistenceContext(unitName = "UserPU")
-
private EntityManager em;
User屬於UserPU這個Persistence Unit,
由於User與Loan他們不屬於同一個Persistence Unit,那麼他們之間就不能用JPQL的語法做聯表查詢,這個時候就只能用createNativeQuery來實現了。
好了現在準備工作都做完了,你可以在你的業務層邏輯中呼叫DAO裡面的方法進行具體的操作啦~
2、迴歸JPA原理論述~註解
現在的ORM技術,如果想會用,那必須搞清楚它裡面大量的註解的意思,JPA也不例外,下面我們解釋上面沒有論述到的比較重要的註解。
1、@AttributeOverrides、@AttributeOverride:
如果某個實體類中Person中定義了一個屬性sex,但是這個實體類中還引用了另一個物件Cat,由於Cat中也有一個屬性sex,資料庫無法存兩個完全一樣的屬性名稱,且也不方便區分使用此註解給Cat的sex屬性重新命名,如下,如果有多個屬性就寫多個@AttributeOverride
-
@AttributeOverrides({
-
@AttributeOverride(name = "sex", column =
-
@Column(name = "CAT_SEX")),
-
@AttributeOverride(name = "name", column =
-
@Column(name = "CAT_NAME"))
-
})
-
private Cat cat;
2、@Basic
這個註解很簡單,用於描述是否立即載入該屬性的資料:fetch = FetchType.LAZY表示懶載入,fetch = FetchType.EAGER表示熱載入;optional=true表示不可為空。如果一個實體類有一個大文字屬性,且其並不急於立即使用該屬性的值,我們應該這麼定義:
-
@Basic(fetch = FetchType.LAZY, optional = false)
-
@Lob
-
private byte[] content;
import javax.persistence.Basic;
import javax.persistence.FetchType;
import javax.persistence.Lob;
這是涉及的幾個類的出處。
3、@Column
設定某一屬性的一些規範,比如下面的定義,要求name屬性:不能為空、不能更新、長度為18,還有其它很多定義,參考API文件。
-
@Column(nullable = false, updatable = false, length=18)
-
private String name;
repayAmount可以為空、精度15位、保留2位小數
-
@Column(nullable = true, precision = 15, scale = 2)
-
private BigDecimal repayAmount;
4、@Enumerated
-
@Enumerated(EnumType.STRING)
-
@Column(nullable = false)
-
private LoanStatus status;
用於將列舉型別對映成列舉對應的字串,如果不設定,那麼該列舉型別的屬性在資料庫中存的值:是按照其定義的順序來設值的,即列舉常量的序數值,型別為整形。
5、@Inheritance、@DiscriminatorColumn、@DiscriminatorValue
在JPA中,實體繼承關係的對映策略共有三種:單表繼承策略(table per class)、Joined策略(table per subclass)和Table_PER_Class策略。一般都用單表在表中通過一列辨別欄位來區別不同類別的實體。具體做法如下:在父類實體的@Entity註解下新增如下的註解:
@Inheritance(Strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name=”辨別欄位列名”)
@DiscriminatorValue(父類實體辨別欄位列值)
舉例如下:
-
@Entity
-
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
-
@DiscriminatorValue("FundRecord")
-
@DiscriminatorColumn(name = "DTYPE")
-
@Table(name = "TB_FUND_RECORD",
-
uniqueConstraints = {
-
@UniqueConstraint(columnNames = {"USER_ID", "type", "orderId"})})
-
public class FundRecord extends RecordScopeEntity {
它有一個子類:
-
@Entity
-
@DiscriminatorValue("FundWithdraw")
-
public class FundWithdraw extends FundRecord {
這樣在資料庫中,這兩個實體類的資料都存在一張表裡面,不過有一個區分的欄位:DTYPE(通過註解@DiscriminatorColumn(name=”辨別欄位列名”)來設定區分的欄位名稱),父類的值為FundRecord,子類是FundWithdraw,通過註解@DiscriminatorValue("FundRecord")來設定的。
6、@JoinColumn
設定外來鍵:在採用這種外來鍵約束的時候我們需要首先考慮一下。根據我們定義的邏輯關係,可以認為是Employee裡有Address這麼一個欄位。那麼就相當於Employee裡要引用到Address的資訊。而按照外來鍵的定義,是一個表引用另外一個表的主鍵。那麼,我們就需要給Address定義一個主鍵,同時我們也要標註一下在Employee裡引用的Address欄位該是什麼名字:
-
@OneToOne(cascade=CascadeType.ALL)
-
@JoinColumn(name="address_id")
-
private Address address;
-
預設值:CascadeType 的空陣列。
-
預設情況下,JPA 不會將任何持續性操作層疊到關聯的目標。
-
如果希望某些或所有持續性操作層疊到關聯的目標,請將 cascade 設定為一個或多個 CascadeType 例項,其中包括:
-
• ALL — 針對擁有實體執行的任何持續性操作均層疊到關聯的目標。
-
• MERGE — 如果合併了擁有實體,則將 merge 層疊到關聯的目標。
-
• PERSIST — 如果持久儲存擁有實體,則將 persist 層疊到關聯的目標。
-
• REFRESH — 如果重新整理了擁有實體,則 refresh 為關聯的層疊目標。
-
REMOVE — 如果刪除了擁有實體,則還刪除關聯的目標。
這裡,我們增加了一個標註@OneToOne(cascade=CascadeType.ALL)和@JoinColumn(name="address_id")。@OneToOne表示他們是一對一的關係,同時cascade表示他們的級聯關係。如果我們仔細觀察一下的話會發現前面應用程式碼裡有一個比較有趣的地方,我們em.persist()只是儲存了employee物件。而對應的Address物件只是設定為employee物件的一個屬性。我們希望是employee物件被儲存到資料庫裡的時候address物件也自動儲存進去。那麼我們就需要設定這個cascade的級聯訪問屬性。否則我們就需要顯式的利用em.persist()來儲存address物件。這也就是為什麼我們要用一個cascade的屬性。
7、@PersistenceContext
獲取實體管理器:
-
@PersistenceContext(unitName = "LendingPU")
-
private EntityManager em;
EntityManager 是用來對實體 Bean 進行操作的輔助類。他可以用來產生/刪除持久化的實體 Bean,通過主鍵查詢 實體 bean,也可以通過 EJB3 QL 語言查詢滿足條件的實體 Bean。實體 Bean 被 EntityManager 管理時,EntityManager 跟蹤他的狀態改變,在任何決定更新實體 Bean 的時候便會把發生改變的值同步到資料庫中。當實體 Bean 從 EntityManager 分離後,他是不受管理的,EntityManager 無法跟蹤他的任何狀態改變。EntityManager 的獲取前面 已經介紹過,可以通過@PersistenceContext 註釋由 EJB 容器動態注入
8、@Temporal
-
• DATE - 等於 java.sql.Date
-
• TIME - 等於 java.sql.Time
-
• TIMESTAMP - 等於 java.sql.Timestamp
在進行實體對映時,有關時間日期型別的型別可以是java.sql包下的java.sql.Date、java.sql.Time 和java.sql.Timestamp,還有java.util包下的java.util.Date 和 java.util.Calendar型別。預設情況下,實體中使用的資料型別是java.sql包下的類,但此時如果要使用java.util包中的時間日期型別,則需要而外標註@Temporal註釋來說明轉化成java.util包中的型別。例如:
-
import java.util.Date;
-
@Temporal(TemporalType.TIMESTAMP)
-
@Column(nullable = true)
-
private Date timeOpen;
後續會陸續新增其他的註解~~~
4、迴歸JPA原理論述~EntityManager
1、重新整理實體refresh()
如果你懷疑當前被管理的實體已經不是資料庫中最新的資料,你可以通過 refresh()方法重新整理實體,容器會把資料 庫中的新值重寫進實體。這種情況一般發生在你獲取了實體之後,有人更新了資料庫中的記錄,這時你需要得到 最新的資料。當然你再次呼叫 find()或 getReference()方法也可以得到最新資料,但這種做法並不優雅。
-
@PersistenceContext
-
protected EntityManager em;
-
...
-
Person person = em.find(Person.class, 2);
-
//如果此時 person 對應的記錄在資料庫中已經發生了改變,可以通過 refresh()方法得到最新資料。
-
em.refresh (person);
2、檢測實體當前是否被管理中contains()
-
@PersistenceContext
-
protected EntityManager em; ...
-
Person person = em.find(Person.class, 2);
-
if (em.contains(person)){
-
//正在被持久化內容管理
-
}else{
-
//已經不受持久化內容管理
-
}
3、分離所有當前正在被管理的實體clear()
在處理大量實體的時候,如果你不把已經處理過的實體從 EntityManager 中分離出來,將會消耗你大量的記憶體。呼叫 EntityManager 的 clear()方法後,所有正在被管理的實體將會從持久化內容中分離出來。 有一點需要說明下,在事務沒有提交前(事務預設在呼叫堆疊的最後提交,如:方法的返回),如果呼叫 clear()方法,之前對實體所作的任何改變將會掉失,所以建議你在呼叫 clear()方法之前先呼叫 flush()方法儲存更改。
4、將實體的改變立即重新整理到資料庫中flush()
當實體管理器物件在一個session bean中使用時,它是和伺服器的事務上下文繫結的。實體管理器在伺服器的事 務提交時提交併且同步它的內容。在一個session bean中,伺服器的事務預設地會在呼叫堆疊的最後提交。當你呼叫 persist( ), merge( )或 remove( )這些方法時,更新並不會立刻同步到資料庫中,直到容器決定重新整理到資料 庫中時才會執行,預設情況下,容器決定重新整理是在“相關查詢”執行前或事務提交時發生,當然“相關查詢”除 find()和 getreference()之外,這兩個方法是不會引起容器觸發重新整理動作的,預設的重新整理模式是可以改變的,具體請 考參下節。如果你需要在事務提交之前將更新重新整理到資料庫中,你可以直接地呼叫 EntityManager.flush()方法。這 種情況下,你可以手工地來重新整理資料庫以獲得對資料庫操作的最大控制。
-
@PersistenceContext
-
protected EntityManager em;
-
...
-
public void updatePerson(Person person) {
-
try {
-
Person person = em.find(Person.class, 2);
-
person.setName("lihuoming");
-
em.merge(person); em.flush();//手動將更新立刻重新整理進資料庫
-
//後面還有眾多修改操作
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
3、迴歸JPA原理論述~JPQL語言
1、Query query = em.createQuery("select count(p) from Person p");
-
Query query = em.createQuery("select count(p) from Person p");
-
Object result = query.getSingleResult();
-
if (result == null || Integer.parseInt(result.toString()) == 0) {
-
}
-
@PersistenceContext
-
protected EntityManager em;
-
private String QueryOrderBy( ){
-
//先按年齡降序排序,然後按出生日期升序排序
-
Query query = em.createQuery("select p from Person p order by p.age desc, p.birthday asc");
-
List result = query.getResultList();
-
StringBuffer out = new StringBuffer("*************** QueryOrderBy 結果列印****************<BR>");
-
if (result!=null){
-
Iterator iterator = result.iterator();
-
while( iterator.hasNext() ){
-
Person person= (Person)iterator.next();
-
out.append(person.getName()+ "<BR>”);
-
}
-
}
-
}
2、上面的查詢是查詢物件的集合,有時候這種方式效率會很低,比如,一個表每條記錄有50個屬性,其實我只想要其中的姓名、年齡和性別,而不是這個人的個人資料等所有資訊,這個時候我們應該查詢部分屬性,提高查詢效率:
-
@PersistenceContext protected EntityManager em; ...
-
private String QueryPartAttribute(){
-
//集合中的元素不再是 Person,而是一個 Object[]物件陣列
-
Query query = em.createQuery("select p.personid, p.name from Person p order by p.personid desc ");
-
List result = query.getResultList();
-
if (result!=null){
-
Iterator iterator = result.iterator();
-
while( iterator.hasNext() ){
-
//取每一行
-
Object[] row = ( Object[]) iterator.next();
-
//陣列中的第一個值是 personid
-
int personid = Integer.parseInt(row[0].toString());
-
//陣列中的第二個值是 name
-
String PersonName = row[1].toString();
-
}
-
}
-
}
3、聚合查詢
-
//獲取最大年齡
-
Query query = em.createQuery("select max(p.age) from Person p");
-
Object result = query.getSingleResult();
-
String maxAge = result.toString();
-
//獲取平均年齡
-
query = em.createQuery("select avg(p.age) from Person p");
-
result = query.getSingleResult();
-
String avgAge = result.toString();
-
//獲取最小年齡
-
query = em.createQuery("select min(p.age) from Person p");
-
result = query.getSingleResult();
-
String minAge = result.toString();
-
//獲取總人數
-
query = em.createQuery("select count(p) from Person p");
-
result = query.getSingleResult();
-
String countperson = result.toString();
-
//獲取年齡總和
-
query = em.createQuery("select sum(p.age) from Person p");
-
result = query.getSingleResult();
-
String sumage = result.toString();
和SQL一樣,如果聚合函式不是select...from的唯一一個返回列,需要使用"GROUP BY"語句。"GROUP BY"應該包 select 語句中除了聚合函式外的所有屬性。
-
@PersistenceContext protected EntityManager em; ...
-
private String QueryGroupByHaving(){
-
//返回人數超過 1 人的性別
-
Query query = em.createQuery("select p.sex, count(p) from Person p group by p.sex having count(*) >?1");
-
//設定查詢中的引數
-
query.setParameter(1, new Long(1));
-
//集合中的元素不再是 Person,而是一個 Object[]物件陣列
-
List result = query.getResultList();
-
Iterator iterator = result.iterator();
-
while( iterator.hasNext() ){
-
//取每一行
-
Object[] row = (Object[]) iterator.next();
-
}
4、批量更新
-
@PersistenceContext
-
protected EntityManager em;
-
...
-
private String QueryBatchUpdate(){
-
//把所有訂單的金額加 10
-
Query query = em.createQuery("update Order as o set o.amount=o.amount+10");
-
//update 的記錄數
-
int result = query.executeUpdate();
-
}
5、結果集分頁
-
Query query = getEntityManager()
-
.createNamedQuery("Invest.listByLoanAndStatus", Invest.class)
-
.setParameter("loanId", loanId)
-
.setParameter("statusList", statusList);
-
query.setFirstResult(pageInfo.getOffset());
-
query.setMaxResults(pageInfo.getSize());
-
query.getResultList();
--------------------- 本文來自 小奕是個胖子 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/zhengchao1991/article/details/70236919?utm_source=copy