美文网首页Spring boot 技术Java 杂谈
JPA操作数据库原来如此简单

JPA操作数据库原来如此简单

作者: jackieonway | 来源:发表于2019-07-31 20:41 被阅读4次

    JPA简介以及优势

    JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体[对象持久化。

    标准化

    JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。

    容器级特性的支持

    JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。

    简单方便

    JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence下的@Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。

    查询能力

    JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

    高级特性

    JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

    整合步骤

    一、引入相关依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
          <!-- 阿里巴巴连接池Druid -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.5</version>
            </dependency>
    
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-core</artifactId>
                <version>5.3.7.Final</version>
            </dependency>
    

    二、SQL语句以及配置连接池信息

    SQL语句
    CREATE DATABASE `jpatest` ;
    

    也许您想说,为啥不写创建表的语句,因为JPA能帮我们根据实体信息生成对应的数据库表,是不是超级方便呢?

    配置JPA信息
    # 数据库基本配置
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jpatest?serverTimezone=Asia/Shanghai
    spring.datasource.username=JPA_TEST(Zujuq4uSPX4ciVivGYDFlSYkxXGJjkcl)
    spring.datasource.password=JPA_TEST(gNaKlGt35SGsU6HkpmJKVVKUldFFncZx)
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    
    spring.jpa.database=MYSQL
    # 显示后台处理的SQL语句,方便在开发阶段调试
    spring.jpa.show-sql=true
    # 自动检查实体和数据库表是否一致,如果不一致则会进行更新数据库表
    spring.jpa.hibernate.ddl-auto=update
    #自动将驼峰命名转换为小写和下划线 userId -> user_id
    spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    #不加这句则默认为myisam引擎
    spring.jpa.database-platform= org.hibernate.dialect.MySQL5InnoDBDialect
    

    数据库用户名和密码如果不知道是怎么回事的话,请参考Spring Boot整合Jasypt增强应用安全这篇文章哦。

    建立实体关系

    
    import lombok.Data;
    
    import javax.persistence.*;
    import java.util.Date;
    
    @Entity //声明这个是一个数据库对应的实体
    @Table(name = "user") //指定表的名字
    @Data //为了节省代码使用了Lombok插件
    public class User {
    
        @Id //声明这个是数据库表的主键Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)  //声明主键的生成方式
        /*
         JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO.
         TABLE:使用一个特定的数据库表格来保存主键。
         SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
         IDENTITY:主键由数据库自动生成(主要是自动增长型)
         AUTO:主键由程序控制(也是默认的,在指定主键时,如果不指定主键生成策略,默认为AUTO)
         */
        @Column(name = "id", unique = true, nullable = false)
        /*
        指定这个是一个数据库字段
               name 属性表示字段名称
               unique 属性表示该字段是否唯一 true : 唯一 false : 不唯一
               注意: 非主键的唯一字段会生成一个唯一索引,命名规格为 UK_随机字符串
               nullable 属性表示该字段是否可空 true :可以为空
               length 属性表示该字段的长度
         */
        private Integer id;
    
        @Column(name = "user_name",length = 32)
        private String userName;
    
        @Column(name = "name",length = 32, unique = true, nullable = false)
        private String name;
    
        @Column(name = "password",length = 32)
        private String password;
    
        @Temporal(TemporalType.TIMESTAMP)
        /*
            该注解表示这个字段是一个时间类型的
            DATE: 表示只存日期 如: 2019-07-31
            TIME: 表示只存时间 如: 19:52:25
            TIMESTAMP: 表示是一个带有时间的日期  如: 2019-07-31 19:52:25
         */
        @Column(name = "gmt_create")
        private Date gmtCreate;
    
        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("{");
            sb.append("\"id\":")
                    .append(id);
            sb.append(",\"userName\":\"")
                    .append(userName).append('\"');
            sb.append(",\"name\":\"")
                    .append(name).append('\"');
            sb.append(",\"password\":\"")
                    .append(password).append('\"');
            sb.append(",\"gmtCreate\":\"")
                    .append(gmtCreate).append('\"');
            sb.append("}");
            return sb.toString();
        }
    }
    
    数据库访问层
    import com.example.demo.jpa.entity.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    import org.springframework.stereotype.Repository;
    
    import java.io.Serializable;
    
    /**
     * @author Jackieonway
     * @version 1.0
     * @interfaceName UserDao
     **/
    @Repository
    public interface UserDao extends JpaRepository<User,Integer>,JpaSpecificationExecutor<User>,Serializable {
    }
    
    

    如果不使用JPA的高级属性的话, 只需要继承JpaRepository<T,ID>接口就可以了,如果要使用更高级的功能则需要继承JpaSpecificationExecutor<T>

    数据服务层
    import com.example.demo.jpa.dao.UserDao;
    import com.example.demo.jpa.entity.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.jpa.domain.Specification;
    import org.springframework.stereotype.Service;
    
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.CriteriaQuery;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Jackieonway
     * @version 1.0
     * @className UserService
     **/
    @Service
    public class UserService {
        @Autowired
        private UserDao userDao;
    
        public Page<User> get(){
            return userDao.findAll(PageRequest.of(0,10,Sort.Direction.DESC,"name","gmtCreate"));
        }
        public Page<User> getAll(){
            List<Sort.Order> orders = new ArrayList<>();
            orders.add(new Sort.Order(Sort.Direction.DESC,"name"));
            orders.add(new Sort.Order(Sort.Direction.ASC,"gmtCreate"));
            return userDao.findAll(PageRequest.of(0,10,Sort.by(orders)));
        }
    
        public Page<User> getAllPage(){
            Specification<User> specification = new Specification<User>() {
                @Override
                public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery,
                    CriteriaBuilder criteriaBuilder) {
                    Predicate p1 = criteriaBuilder.like(root.get("name").as(String.class), "%12%");
                    Predicate p2 = criteriaBuilder.like(root.get("password").as(String.class), "12%");
                    Predicate p3 = criteriaBuilder.like(root.get("userName").as(String.class), "12%");
                    criteriaQuery.where(criteriaBuilder.and(p1,criteriaBuilder.or(p2,p3))); //where p1 and ( p2 or p3);
                    criteriaQuery.orderBy(criteriaBuilder.asc(root.get("userName").as(String.class)),
                        criteriaBuilder.desc(root.get("name").as(String.class))); // order by user_name asc,name desc
                    return criteriaQuery.getRestriction();
                }
            };
            return userDao.findAll(specification,PageRequest.of(0,10));
        }
    }
    

    好了,下面就建一个测试类来验证下整合的结果吧

     @Autowired
        private UserService userService;
        @Test
        public void contextLoads() {
            Page<User> page = userService.get();
            System.out.println("========= "+page);
        }
        @Test
        public void testGetAll() {
            Page<User> page = userService.getAll();
            System.out.println("========= "+page);
        }
    
        @Test
        public void testGetAllPage() {
            Page<User> page = userService.getAllPage();
            System.out.println("========= "+page);
        }
    
    控制台输出结果
    Hibernate: select user0_.id as id1_0_, user0_.gmt_create as gmt_crea2_0_, user0_.name as name3_0_, user0_.password as password4_0_, user0_.user_name as user_nam5_0_ from user user0_ order by user0_.name desc, user0_.gmt_create desc limit ?
    ========= Page 1 of 0 containing UNKNOWN instances
    

    由于我们配置了运行时数据执行的语句,控制台就会打印出执行的语句,生产环境中可以将该参数取消,减少日志输出。

    相关文章

      网友评论

        本文标题:JPA操作数据库原来如此简单

        本文链接:https://www.haomeiwen.com/subject/gkjxdctx.html