JPA使用详解及示例
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();