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();