JPQL查询语言

如果接触过C#语言应该知道强大的Linq语法,Java中虽然没有Linq,但JPA中的JPQL也是类似的一种结构化查询语言,在进行ORM查询时,可以当作“低配版”Linq使用。简单来说,JPQL就是JPA规范中定义的一种面向对象的结构化查询语言,能够定义复杂的查询逻辑,JPQL用于操作实体对象,而非直接操作关系型数据库表,且使用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();

此时如果返回多条或没有结果会抛出NonUniqueResultExceptionNoResultException异常。

JPQL标量查询

有时我们查询的不是实体而是一个标量,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的更新和删除。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap