如果接触过C#语言应该知道强大的Linq语法,Java中虽然没有Linq,但JPA中的JPQL也是类似的一种结构化查询语言,在进行ORM查询时,可以当作“低配版”Linq使用。简单来说,JPQL就是JPA规范中定义的一种面向对象的结构化查询语言,能够定义复杂的查询逻辑,JPQL用于操作实体对象,而非直接操作关系型数据库表,且使用JPQL具有屏蔽底层数据库差异的优势。
下面例子代码中,我们使用JPQL实现了查询名字叫汤姆的学生Student
实体。
package com.gacfox.netstore.ejb;
import com.gacfox.netstore.api.DemoService;
import com.gacfox.netstore.ejb.entity.Student;
import org.jboss.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.List;
@Stateless
public class DemoServiceImpl implements DemoService {
@PersistenceContext(unitName = "jpa-netstorer-pu")
private EntityManager entityManager;
private static final Logger logger = Logger.getLogger(HelloServiceImpl.class);
@Override
public void demo() {
String jpql = "select s from Student s where s.name = :name";
TypedQuery<Student> query = entityManager.createQuery(jpql, Student.class);
query.setParameter("name", "汤姆");
List<Student> studentList = query.getResultList();
logger.infov("Result: {0}", studentList.size());
}
}
JPQL的语法和SQL类似,但它操作的是实体对象,也就是说JPQL中操作的是Java类型和类的属性名,如果你在JPQL中写了数据库表名或字段名肯定会报错。使用JPQL查询实体我们通常使用createQuery(String qlString, Class<T> resultClass)
重载并获取TypedQuery
,它是一个带泛型的查询结果。
JPA中JPQL可以动态的绑定参数,上面写法中:name
就是一个命名参数占位符,后面的代码里使用了query.setParameter("name", "汤姆")
进行了参数绑定,另一种参数绑定语法是类似... where s.name = ?1
,这种写法比较丑陋且可读性较差,个人不推荐使用。
如果我们确定查询结果只有一条,也可以使用getSingleResult()
方法。
String jpql = "select s from Student s where s.id = :id";
TypedQuery<Student> query = entityManager.createQuery(jpql, Student.class);
query.setParameter("id", 1L);
Student student = query.getSingleResult();
此时如果返回多条或没有结果会抛出NonUniqueResultException
或NoResultException
异常。
有时我们查询的不是实体而是一个标量,JPQL也支持这种查询,下面例子我们统计学生Student
实体的数量。
package com.gacfox.netstore.ejb;
import com.gacfox.netstore.api.DemoService;
import org.jboss.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
@Stateless
public class DemoServiceImpl implements DemoService {
@PersistenceContext(unitName = "jpa-netstorer-pu")
private EntityManager entityManager;
private static final Logger logger = Logger.getLogger(HelloServiceImpl.class);
@Override
public void demo() {
String jpql = "select count(s) from Student s";
TypedQuery<Long> query = entityManager.createQuery(jpql, Long.class);
long count = query.getSingleResult();
logger.infov("Result: {0}", count);
}
}
JPQL和SQL不同,我们可以直接使用.
点操作符取出操作的实体对象的嵌套实体对象,下面例子我们查询ID为1的教室中的所有学生实体。
package com.gacfox.netstore.ejb;
import com.gacfox.netstore.api.DemoService;
import com.gacfox.netstore.ejb.entity.Student;
import org.jboss.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.List;
@Stateless
public class DemoServiceImpl implements DemoService {
@PersistenceContext(unitName = "jpa-netstorer-pu")
private EntityManager entityManager;
private static final Logger logger = Logger.getLogger(HelloServiceImpl.class);
@Override
public void demo() {
String jpql = "select s from Student s where s.room.id = :id";
TypedQuery<Student> query = entityManager.createQuery(jpql, Student.class);
query.setParameter("id", 1L);
List<Student> studentList = query.getResultList();
logger.infov("Result: {0}", studentList.size());
}
}
JPA中分页查询需要配合使用setFirstResult()
和setMaxResults()
方法实现,下面是一个例子。
package com.gacfox.netstore.ejb;
import com.gacfox.netstore.api.DemoService;
import com.gacfox.netstore.ejb.entity.Student;
import org.jboss.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.List;
@Stateless
public class DemoServiceImpl implements DemoService {
@PersistenceContext(unitName = "jpa-netstorer-pu")
private EntityManager entityManager;
private static final Logger logger = Logger.getLogger(HelloServiceImpl.class);
@Override
public void demo(int pageNum, int pageSize) {
String jpql = "select s from Student s";
TypedQuery<Student> query = entityManager.createQuery(jpql, Student.class);
query.setFirstResult((pageNum - 1) * pageSize);
query.setMaxResults(pageSize);
List<Student> studentList = query.getResultList();
logger.infov("Result: {0}", studentList.size());
}
}
注意JPA中setFirstResult()
的第一个参数是从0开始算的,而页码一般从1开始,因此这里使用了pageNum - 1
的写法。
JPQL也可以用于执行非查询操作,下面例子我们使用JPQL删除了ID为1
的数据记录。
package com.gacfox.netstore.ejb;
import com.gacfox.netstore.api.DemoService;
import org.jboss.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Stateless
public class DemoServiceImpl implements DemoService {
@PersistenceContext(unitName = "jpa-netstorer-pu")
private EntityManager entityManager;
private static final Logger logger = Logger.getLogger(HelloServiceImpl.class);
@Override
public void demo() {
String jpql = "delete from Student s where s.id = :id";
Query query = entityManager.createQuery(jpql);
query.setParameter("id", 1L);
query.executeUpdate();
}
}
注意对于更新和删除操作,Query
不可以使用之前的TypedQuery
。代码中,我们需要调用query.executeUpdate()
执行JPQL的更新和删除。