spring data JPA 养吾剑总结

spring data JPA 养吾剑总结

jpa是sun的orm规范,操作api全都是抽象类和接口,hibernate等框架对这套规范进行了实现。今天学习的就是基于hibernate的jpa。现如今hibernate原生api基本已经被jpa取代,所以不再讲解原生hibernate。

spring又对jpa规范的代码进行了封装,相当于hibernateTemplate和hibernate原生api的关系,这就是spring data JPA 。市场上原生jpa和springdatajpa都有运用,所以先介绍jpa,再介绍springdatajpa

基本配置

maven配置

<dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
    </dependencies>

配置文件resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <!--unit为持久化单元节点:
        name:单元名称
        type:事务管理方式
            JTA:分布式事务管理
            RESOURCE_LOCAL 单表本地事务管理
    -->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
    <!--jpa的实现方式-->
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <!--数据源信息-->
    <properties>
        <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="javax.persistence.jdbc.user" value="root"/>
        <property name="javax.persistence.jdbc.password" value="JIANGkui1"/>
        <!--可选配置,配置jpa实现方的配置信息,也即hibernate的可选配置-->
        <property name="hibernate.show_sql" value="true"/>
        <property name="hibernate.format_sql" value="true"/>
        <property name="hibernate.hbm2ddl.auto" value="validate"/>
    </properties>
</persistence-unit>
</persistence>

实体类映射

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
//    private Integer userid;
    private String name;
    private  Float money;

    @ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.PERSIST,CascadeType.MERGE})
    @JoinColumn(name = "userid")
    @BatchSize(size = 10)
    private User user;
}

@Entity 相当于class标签,指定当前类是一个实体类,name属性为实体名称。默认为类的非限定性类名,用于hql查询。
@Table name属性指定映射的表名。默认与实体名称相同。

@Id用在主键id属性上,或者其get方法上,表示当前属性将对应数据库中的主键。
@GeneratedValue 将指定主键值的来源,其属性strategy用于指定主键的生成策略。其值为系统定义好的四种策略之一。默认为AUTO(native)。尽量写@GeneratedValue(strategy = GenerationType.IDENTITY)不会报错。
@Column name/nullable/length/insertable/updateable
precision和scale表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数;
@Basic:表示该字段将映射到DB中,是属性的默认注解;
@Transient:表示该字段不映射到DB中,该注解无属性;
@Temporal(TemporalType.DATE) 指定输出日期的格式

表关联:
@OneToMany表明是一对多关联关系;属性如下:
targetEntity:指明该属性所关联的类。
cascade:指定级联类型。其为数组,使用多种级联,则可使用{}赋值。其值为Cascade常量。
mappedBy:string类型,写对方属性名,等价于inverse=true,放弃外键维护。使用该属性后将不能够再使用@JoinColumn注解。
fetch = FetchType.EAGER
@JoinColumn指明该属性所关联的外键;设置该属性后默认拥有外键维护权

@ManyToMany注解 其会自动生成一个中间表,表名为两个关联对象的映射表名的联合:表1_表2。只不过,谁在维护关联关系,谁的表名在前。字段名与谁在维护关联关系相关。谁在维护关联关系,谁的表名将出现在第一个字段名中,而该类的关联属性名将出现在第二个字段名中。字段名分别为表名_id与关联属性名_id。
@JoinTable 指定中间表的一些设置
name:中间表名称
joinColumns,本实体类在中间表的外键列名字,@JoinColumn(name = “studentId”)这样写。
inverseJoinColumns 对方表在中间表外键列的名字

一对多双向关联范例:有实体外键,即多一方定义JoinColumn,另一方mappedBy即可。

    @ManyToOne(fetch = FetchType.LAZY,cascade ={CascadeType.PERSIST,CascadeType.MERGE})
    @JoinColumn(name = "userid")
    private User user;

    @OneToMany(fetch = FetchType.LAZY,mappedBy = "user",cascade = CascadeType.ALL)
    private Set<Account> accounts =new HashSet<Account>();

多对多双向关联范例:

在逻辑上的主动方定义JoinTable即可,这样,被动方是不能管理外键的。

    @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
    @JoinTable(name="user_role",joinColumns = @JoinColumn(name = "uid"),inverseJoinColumns = @JoinColumn(name = "rid"))
    private Set<Role> roles=new HashSet<Role>();

    @ManyToMany(mappedBy = "roles" , cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private Set<User> users=new HashSet<User>();

使用api

public class Jpatest {

    static EntityManager manager;
    static EntityTransaction transaction;
    static EntityManagerFactory factory;

    @BeforeClass
    public static void  init(){
//        Configuration configuration=new Configuration().configure();
//        managerFactory managerFactory = configuration.buildmanagerFactory();
//        manager = managerFactory.getCurrentmanager();
//        transaction = manager.beginTransaction();
        factory = Persistence.createEntityManagerFactory("myJpa");
        manager = factory.createEntityManager();
        transaction = manager.getTransaction();
        transaction.begin();
    }
    @AfterClass
    public static void close(){
//        transaction.commit();
        manager.close();
        transaction.commit();
        manager.close();
        factory.close();
    }
    @Test
    public void testsave(){
        Account account = new Account();
        account.setMoney(2001f);
        account.setName("mybatis-annotasion");
        User user = new User();
        user.setUserName("天皇号");
        user.getAccounts().add(account);
        account.setUser(user);
//        manager.save(user);
        manager.persist(account);
    }
}

EntityManager对象基本删增改查:
presist、merge、remove、find/getRefrence
和hibernate:save/update/delete/get/find基本一一对应。

概念介绍

【三态】

1)瞬时态(transient)未保存
瞬时态也称为临时态或者自由态,瞬时态的对象是由 new 关键字开辟内存空间的对象,不存在持久化标识 OID(相当于主键值),且未与任何的 manager 实例相关联,在数据库中也没有记录,失去引用后将被 JVM 回收。瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系。
2)持久态(persistent)已保存
持久态的对象存在一个持久化标识 OID,当对象加入到 manager 缓存中时,就与 manager 实例相关联。它在数据库中存在与之对应的记录,每条记录只对应唯一的持久化对象。需要注意的是,持久态对象是在事务还未提交前变成持久态的。
3)脱管态(detached)已断线
脱管态也称离线态或者游离态,当持久化对象与 manager 断开时就变成了脱管态,但是脱管态依然存在持久化标识 OID,只是失去了与当前 manager 的关联。需要注意的是,脱管态对象发生改变时 Hibernate 是不能检测到的。

注意:持久化态的对象在commit时,会和缓存做比较,当内容发生变化时,会自动向数据库发送update的sql语句。

【一级缓存】
1、保存时默认将数据库返回的已持久化对象加入缓存(不会等提交,而是马上发sql)。
2、查询时先查缓存,缓存没有,才会查数据库并加入缓存。
3、manager关闭时,缓存清空,clear()方法调用后,也将清空缓存,此后提交也不会再自动发更新语句。

刷出缓存(将缓存和快照的改变发送到数据库):
1、 commit()方法执行时,会进行保存内容和缓存的对比,智能发送update的sql语句。
2、select时,如果缓存中的对象已经发生变化,会更新缓存。
3、调用manager.flush方法,会默认刷新缓存,强制将缓存与快照的变化更新的数据库。
3、调用.refresh方法,会重新查询数据库,用数据库值覆盖缓存。

【事务管理】
thread
开启manager线程绑定,然后所有获取manager 的地方都用:
manager = managerFactory.getCurrentmanager();
保证一个线程获取的都是同一个manager,即可完成事务管理。

查询select

【JPQL查询】
1、别名查询:
from Account a;select a from Account;
2、排序查询:
from User order by id,userName desc ;默认升序
3、条件查询:

        Query query = manager.createQuery("from Account where name like ?1");
        query.setParameter(1,"杨%");位置匹配
        //Query query = manager.createQuery("from Account where name like :name");
        //query.setParameter("name","杨%");名字匹配

4、投影查询:

        Query query = manager.createQuery("select u.userName,u.sex from User u");
        List<Object[]> list = query.list();

​ 第二种,个别封装:

        Query query = manager.createQuery("select new User (u.userName,u.birthday,u.sex) from User u");
        List<User> list = query.list();

5、分页查询
query.setFetchSize(0);
query.setMaxResults(5);
返回从1-5。公式为pagenum-1)*pagesize,pagesize
6、聚合函数

Long re =(Long) manager.createQuery("select count() from User ").uniqueResult();
	List list = manager.createQuery("select 	count(distinct userName),address from User group by address").list();

7、多表连接

	//内连接:
	List<Object[]> list = manager.createQuery("from Account a inner join a.user").list();//注意,必须使用别名查询。	
//迫切内连接,封装成对象,会有封装重复问题
	List<Account> list = manager.createQuery("from Account a inner join fetch a.user").list();
	select distinct a from Account a inner join fetch a.user //这样就没有封装重复问题了

【离线条件查询】

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
detachedCriteria.add(Restrictions.like("userName","杨%"));
Criteria criteria = detachedCriteria.getExecutableCriteria(manager);
List<User> list = criteria.list();

【原生sql查询】

        NativeQuery sqlQuery = manager.createSQLQuery("select * from user");
        sqlQuery.addEntity(User.class);
        List<User> list = sqlQuery.list();

【qbc查询】
通过criteriaQuery.where/from/orderby/gropyby来指定具体的语句
通过CriteriaBuilder.like/or/and来指定具体的条件。

//        获取工厂对象
        CriteriaBuilder criteriaBuilder = manager.getCriteriaBuilder();
//        获取针对某个表的条件对象,相当于指定返回值的封装类型
        CriteriaQuery<Account> criteriaQuery = criteriaBuilder.createQuery(Account.class);
//        指定根条件,相当于写from语句
        Root<Account> root = criteriaQuery.from(Account.class);
//        指定查询条件,相当于写where语句,多条件用工厂的and或or链接
        Predicate equal1 = criteriaBuilder.like(root.<String>get("name"),"杨%");
        Predicate lt1 = criteriaBuilder.gt(root.<Number>get("money"), 1000);
        criteriaQuery.where(criteriaBuilder.and(equal1,lt1));
//        指定排序
        criteriaQuery.orderBy(criteriaBuilder.desc(root.get("id")), criteriaBuilder.asc(root.get("userid")));

//       使用manager执行查询语句,与hql一致了
        Query<Account> query = manager.createQuery(criteriaQuery);
        query.setFetchSize(0);
        query.setMaxResults(5);
        List<Account> accounts = query.list();

补充,旧版写法:

 Configuration config = new Configuration().configure();
        // 2.通过manager获得Criteria对象
        Criteria criteria = manager.createCriteria(User.class);
        // 3.使用Restrictions的eq方法设定查询条件为name="zhangsan"
        // 4.向Criteria对象中添加查询条件
        criteria.add(Restrictions.eq("name", "zhangsan"));
        // 5.执行Criterita的list()方法获得结果
        List<User> list = criteria.list();

springDataJpa

springDataJpa的好处是可以写接口的方式快速实现操作Dao,遮蔽entityManage的操作,这样操作体验就类似有mybatis-plus加持的mybatis了。

基础配置

maven依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <!--SPRING-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <!--hibernate-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.1.Final</version>
        </dependency>
        <!--springdata jpa-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.6</version>
        </dependency>

        <!--log-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>
    </dependencies>

使用spirng配置dao的所有配置,包括jpa的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa  http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
">
    
    <context:component-scan base-package="htyy"/>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <tx:annotation-driven></tx:annotation-driven>

    <!--创建工厂对象,交给spring管理-->
    <bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--配置实体类所在包,用于扫描entity等映射注解-->
        <property name="packagesToScan" value="htyy.domain"/>
        <!--jpa实现方式-->
        <property name="persistenceProvider">
                <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>
        <!--配置hibernate的一些可选配置-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="false"/>
                <property name="database" value="MYSQL"/>
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                <property name="showSql" value="true"/>

            </bean>
        </property>
        <!--jpa的方言,即高级特性-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
      <!--配置hibernate自己的属性-->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">validate</prop>
            </props>
        </property>
    </bean>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="user" value="root"></property>
        <property name="password" value="JIANGkui1"></property>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" >
        <property name="entityManagerFactory" ref="entityManagerFactoryBean"/>
    </bean>
    <!--spring整合datajpa,也即自动扫描dao接口,创建实现类-->
    <jpa:repositories base-package="htyy.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactoryBean"/>
</beans>

编写dao接口,继承jpa的含有默认方法的接口类

/**
 * JpaRepository<Account,Integer> 封装了基本crud操作,泛型为要操作的实体对象和该实体对象的主键类型
 *  JpaSpecificationExecutor<Account> 封装了复杂查询(分页等)
 */
@Repository
public interface IAccountDao extends JpaRepository<Account,Integer>, JpaSpecificationExecutor<Account> {
}

此时自己编写的iaccountdao已经具有所有方法,如:
accountDao.findById(3); 返回一个代理对象,可以通过get或者orelse得到account对象,建议后者。
accountDao.findById(24).orElse(null);标准的orelse方法得到实体类
accountDao.getOne(1); lazy查询的方法,延迟加载的方式得到代理对象。
accountDao.deleteById(2);accountDao.delete(account);删除,可以根据id,或者删除查询到的实体。
accountDao.findAll();查询所有。
accountDao.save(account) 保存或更新都是调用的这个方法,有id更新,无id保存。
accountDao.count() 返回一个long类型的总数量。
accountDao.existsById(4) 返回一个boolean,判断是否存在该id的值。

select查询

【hql查询(jpaql查询)】
使用@Query注解在接口方法名上写hql语句即可:

    @Query("from Account where name like ?1")
    public List<Account> findByname(String name);

也可以使用@Modifying+@Query表示要写update语句

@Query("update Account set name = ?1")
    @Modifying
    public List<Account> findByname(String name);

注意,query注解有一个nativeQuery参数,默认为false,true代表使用sql查询,查询的结果不能自动封装,只能使用list<object[]>进行接收。

参数解析的方式有如下几种:
1、按位置获取?1 ?2
2、按名称获取:name :money,此时需要使用@param注解在方法中进行名称匹配:

@Param("name") String name,@Param("money") String money

3、类似mybatis,使用#{#表达式}

   @Query("from Account where name like :#{#account.name} and money >= :#{#account.money}")
    public List<Account> findByExample(@Param("account") Account account);

【方法名称规则查询】
如果按照springdatajpa固定的方法规则,那么会自动給该方法进行代理实现。
1、findBy+规范属性名(属性名称首字母大写),即可完成一个from entity where fieldname=?的查询。
public List findByName(String name);
2、findBy+规范属性名+查询方式(Like/isnull),可以指定查询方式是=,like,isnull。
3、多参数情况下,findBY+(属性名查询)+And/Or+(属性名查询)即可,排序等也可以写在后面:
findByNameLikeAndMoneyAfterOrderByMoney

【复杂条件查询】

    Optional<T> findOne(@Nullable Specification<T> var1); //查询一个
    List<T> findAll(@Nullable Specification<T> var1);//查询多个
    Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);//带分页,返回pageinfo对象
    List<T> findAll(@Nullable Specification<T> var1, Sort var2);//可排序
    long count(@Nullable Specification<T> var1);//返回count的数量

Specification是一个接口,使用时应该使用内部类或lambd表达式自定义自己的实现类。

List<Account> all = accountDao.findAll((Specification<Account>) (root, query, cb) ->
                cb.and(cb.like(root.get("name"), "杨%"),cb.gt(root.get("money"),3000f)));

Predicate toPredicate(Root var1, CriteriaQuery<?> var2, CriteriaBuilder var3);实现该方法。
可以发现,实际上该接口就是对cb、cq、root这三个qbc查询的封装,编写好规则后,使用时会默认传入。最终,只需要返回cb对象合成的一个predicate条件即可。

//sort是一个排序类:
        Sort orders = new Sort(Sort.Direction.DESC, "money", "id");
        Sort orders1 = orders.and(new Sort(Sort.Direction.ASC, "name"));

//pageable是一个接口,我们需要使用它的实现类pagerequest:
PageRequest pageRequest = PageRequest.of(page:2, size:1, sort:orders1);
//三个参数分别是当前页(从0开始),每页数量,排序条件。

Page可用方法、属性如下:
getTotalElements() 当前总项目数
getTotalpages 当前总页数
getSize 得到当前设置的每页容量
getnumber 得到当前页,从0开始
getContent 得到内置的list

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值