hibernate 基本知识

    技术2022-07-13  80

    https://www.jianshu.com/p/d6e87963d39e

    SpringData JPA是spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,可以使开发者使用极简的代码实现对数据库的访问和操作。它提供了包括增删改查等在内的基本功能,且易于扩展。

    springdata jpa、jpa和hibernate三者关系

    通俗来讲springdata jpa是对jpa规范的一层封装,hibernate实现了jpa规范。

    java代码----->springdata jpa ------>jpa规范------>hibernate------>jdbc ----->mysql数据库

     

    graph LR A[java代码] -->B(spring data jpa) B --> |jpa规范| C(hibernate) C -->|jdbc| D(mysql数据库)

    我们使用java代码调用springdata jpa的api,springdata jpa封装了jpa规范,并且内部使用的是hibernate实现,hibernate封装了jdbc进行数据库操作。

    入门案例

    1、创建工程,导入依赖

     

    compile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.3.Final' compile group: 'org.hibernate', name: 'hibernate-c3p0', version: '5.4.3.Final' compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16' compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '2.1.9.RELEASE' testCompile group: 'org.springframework', name: 'spring-test', version: '5.1.8.RELEASE'

    2、编写spring配置文件

    配置spring相关

    数据源信息

    jpa的实现方式

    配置要用到的实体类

    配置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:jpa="http://www.springframework.org/schema/data/jpa" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--spring--> <!--配置spring的注解扫描--> <context:component-scan base-package="com.lxf"/> <!--spring data jpa--> <!--整合spring data jpa--> <jpa:repositories base-package="com.lxf.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" /> <!--创建实体管理器工厂,交给spring管理--> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!--配置数据源--> <property name="dataSource" ref="dataSource"/> <!--配置要扫描的包,实体所在包--> <property name="packagesToScan" value="com.lxf.entity"/> <!--配置jpa的实现方--> <property name="persistenceProvider"> <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/> </property> <!--jpa的实现方的配置--> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <!--数据库类型--> <property name="database" value="MYSQL"/> <!--控制台显示sql语句--> <property name="showSql" value="true"/> <!--是否自动创建数据库表--> <property name="generateDdl" value="true"/> <!--数据库方言--> <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> </bean> </property> <!--jpa方言:高级特性--> </bean> <!--数据源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study?serverTimezone=GMT"/> <property name="user" value="root"/> <property name="password" value="crystal1024"/> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> </bean> <!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!--声明式事务--> </beans>

    3、创建实体类,编写实体类和数据库表关系映射

    参考JPA规范。

    4、编写dao层接口

    需要继承两个接口

    JpaRepository:封装了增删改查分页排序等基本操作,具体可以看JpaRepository的父类graph TB A[Repository] -->B(CrudRepository) B --> C(PagingAndSortingRepository) C -->D(JpaRepository) JpaSpecificationExecutor:封装了标准查询

    提供相应的泛型

    JpaRepository 操作的实体类型实体中主键类型JpaSpecificationExecutor 操作的实体类型

     

    public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> { } 会通过动态代理自动生成相应方法

    5、测试

     

    @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring_data_jpa.xml") public class Test { @Autowired private UserDao userDao; @org.junit.Test public void textSave(){ User user = new User(); user.setName("小红"); user.setAge(22); User userResult = userDao.save(user); System.out.println(userResult); } }

    User{id=4, name='小红', age=22, sex=null, address='null', phone='null'}

    操作数据库

    调用spring data jpa的api

    插入/更新

    save方法:传入的实体对象有主键则更新,没有主键则插入。

     

    @org.junit.Test public void testSave(){ User user = new User(); user.setName("小红"); user.setAge(22); User userResult = userDao.save(user); System.out.println(userResult); }

    删除

    delete系列方法

     

    @org.junit.Test public void testDelete(){ userDao.deleteById(2); }

    查询

    count:统计

    exists系列方法:数据库中是否存在

    find系列方法:立即加载

    getOne:延迟加载,返回的是一个动态代理对象

     

    @org.junit.Test public void testFindOne(){ // Optional<User> user = userDao.findById(2); // System.out.println(user.get()); User user = userDao.getOne(2); System.out.println(user); } @org.junit.Test public void testApi(){ long count = userDao.count(); boolean b = userDao.existsById(2); }

    语句操作

    除了调用spring data jpa内置的api,我们也可以在dao接口中定义我们自己的方法,通过@Query声明jpql或sql语句。

    @Query value:数据库操作语句nativeQuery:是否是原生查询,默认false,即默认使用jpql查询@Modifying:声明当前是一个更新操作,需要修改数据库数据。 只能用于void或int/Integer的返回类型因为需要修改数据库数据,未防止修改失败造成未知后果,需要搭配事务管理来是使用@Transactional:添加事务管理支持 一般需要设置rollbackFor或者noRollbackFor,来表示什么情况下进行事务回滚@Rollback:是否可以回滚,默认true

    jpql查询

     

    public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> { @Query(value = "from User where name = :name and age = :age") public User findUserByName(@Param("name") String userName,@Param("age") int age); }

    jpql更新

     

    @Query(value = "update User set name = :name where id = :id") @Modifying public Integer updateNameById(@Param("id") int id,@Param("name") String userName); @org.junit.Test @Transactional(rollbackFor = Exception.class) //@Rollback(value = false)//如果设置为fasle,即使发生异常也不会回滚 public void testJpql(){ User user = userDao.findUserByName("lili",18); System.out.println(user); userDao.updateNameById(user.getId(),"lili_2"); }

    原生sql语句查询

     

    public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> { @Query(value = "select * from user where name = :name and age = :age",nativeQuery = true) public User findUserByName(@Param("name") String userName,@Param("age") int age); }

    约定规则查询

    spring data jpa制定了一些约定,如果按照这些约定来定义方法名,则会自动解析出sql语句。

    findBy + 属性名 + 查询方式 + (And|Or) + 属性名 + 查询方式...

    查询方式方法命名sql where字句AndfindByNameAndPwdwhere name= ? and pwd =?OrfindByNameOrSexwhere name= ? or sex=?Is,EqualsfindById,findByIdEqualswhere id= ?BetweenfindByIdBetweenwhere id between ? and ?LessThanfindByIdLessThanwhere id < ?LessThanEqualsfindByIdLessThanEqualswhere id <= ?GreaterThanfindByIdGreaterThanwhere id > ?GreaterThanEqualsfindByIdGreaterThanEqualswhere id > = ?AfterfindByIdAfterwhere id > ?BeforefindByIdBeforewhere id < ?IsNullfindByNameIsNullwhere name is nullisNotNull,NotNullfindByNameNotNullwhere name is not nullLikefindByNameLikewhere name like ?NotLikefindByNameNotLikewhere name not like ?StartingWithfindByNameStartingWithwhere name like '?%'EndingWithfindByNameEndingWithwhere name like '%?'ContainingfindByNameContainingwhere name like '%?%'OrderByfindByIdOrderByXDescwhere id=? order by x descNotfindByNameNotwhere name <> ?InfindByIdIn(Collection<?> c)where id in (?)NotInfindByIdNotIn(Collection<?> c)where id not in (?)TruefindByAaaTuewhere aaa = trueFalsefindByAaaFalsewhere aaa = falseIgnoreCasefindByNameIgnoreCasewhere UPPER(name)=UPPER(?)

    简单挑几个示例:

     

    public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> { public User findByName(String name); public User findByNameLike(String name); public User findByNameLikeAndAge(String name, int age); public List<User> findByIdBetween(int idMin, int idMax); } @org.junit.Test public void testName(){ User user1 = userDao.findByName("tom"); System.out.println(user1); User user2 = userDao.findByNameLike("t%"); System.out.println(user2); User user3 = userDao.findByNameLikeAndAge("tom",18); System.out.println(user3); List<User> users = userDao.findByIdBetween(1, 3); users.forEach(new Consumer<User>() { @Override public void accept(User user) { System.out.println(user); } }); }

    Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where user0_.name=? User{id=3, name='tom', age=18, sex=1, address='null', phone='null'} Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where user0_.name like ? escape ? User{id=3, name='tom', age=18, sex=1, address='null', phone='null'} Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where (user0_.name like ? escape ?) and user0_.age=? User{id=3, name='tom', age=18, sex=1, address='null', phone='null'} Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.age as age3_0_, user0_.name as name4_0_, user0_.phone as phone5_0_, user0_.sex as sex6_0_ from user user0_ where user0_.id between ? and ? User{id=2, name='lili2', age=18, sex=1, address='null', phone='null'} User{id=3, name='tom', age=18, sex=1, address='null', phone='null'}

    标准查询(Specification)

    我们上面提到过,springdata jpa的dao层一般继承2个接口JpaRepository和JpaSpecificationExecutor。JpaRepository封装了crud、统计、排序、分页的常见操作,而JpaSpecificationExecutor基于JPA的criteria查询封装了另一种查询方式,我们之前一直在使用JpaRepositoru中的方法,下面来看下JpaSpecificationExecutor接口,它里面只提供了5个方法:

     

    public interface JpaSpecificationExecutor<T> { //查询一个 Optional<T> findOne(@Nullable Specification<T> spec); //查询全部 List<T> findAll(@Nullable Specification<T> spec); //查询全部 提供分页功能 Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable); //查询全部,提供排序功能 List<T> findAll(@Nullable Specification<T> spec, Sort sort); //统计 long count(@Nullable Specification<T> spec); }

    可以看到,这5个方法有个共同点,接收一个Specification参数。

    Specification

    Specification是对JPA规范中Root、CriteriaQuery、CriteriaBuilder的一层封装,用于构建过滤条件。实例化Specification需要实现它的toPerdicate方法:

     

    //参数含义在我的另一文JPA规范中有介绍,简单说来Root用于获得查询属性,CriteriaBuilder用于构建过滤条件,CriteriaQuery用于指定最终查询语句,这里一般不会使用,默认为where语句。 Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);

    注意这里创建出来的是where查询语句。

    来个简单示例,查询表中年龄大于等于18的所有河南人:

     

    @Test public void test(){ Specification<User> specification = new Specification<User>() { @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { //分别构造各个单属性的过滤条件 Predicate namePredicate = criteriaBuilder.like(root.get("address"), "河南%"); Predicate agePredicate = criteriaBuilder.ge(root.get("age"), 18);//大于等于 //组合成最终的过滤条件 Predicate predicate = criteriaBuilder.and(namePredicate, agePredicate); return predicate; } }; //查询 List<User> users = userDao.findAll(specification); users.forEach(new Consumer<User>() { @Override public void accept(User user) { System.out.println(user); } }); }

    如果要添加排序和分页,可以使用Sort和Pageable。

    Sort:排序

     

    Sort sort = new Sort(Sort.Direction.DESC,"id");//排序属性可以设置多个 List<User> users = userDao.findAll(specification,sort); Pageable:分页,是一个接口,可以通过PageRequest构建实例。

     

    Sort sort = new Sort(Sort.Direction.DESC,"id"); //Pageable pageable = PageRequest.of(0,10);//pageIndex,pageSize Pageable pageable = PageRequest.of(0,10,sort); Page<User> users = userDao.findAll(specification, pageable); users.forEach(new Consumer<User>() { @Override public void accept(User user) { System.out.println(user); } });

    spring boot中的springdata jpa配置

    application.yaml

     

    spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/study?serverTimezone=GMT username: root password: crystal1024 jpa: show-sql: true hibernate: ddl-auto: update

    作者:风少侠 链接:https://www.jianshu.com/p/d6e87963d39e 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

     

    Processed: 0.019, SQL: 9