本人更推荐于学习mybatis,因为所跟的项目技术栈是JPA,所以进行JPA的学习总结,下面是mybatis学习总结,嘿嘿,开篇劝退。
两万字使用通俗语言讲解mybatis_狗头实习生的博客-CSDN博客
JPA与JDBC的关系
相同点:
1.都跟操作数据库有关、JPA是JDBC的升级版
2.JDBC和JPA都是一组模范接口
3.都是由sun官方推出
不同点:
1.JDBC是由各个关系型数据库实现的,JPA是由ORM框架实现(ORM:对象关系映射)
2.JDBC使用SQL语句和数据库通信,JPA面向对象,通过ORM框架来生成SQL进行操作
3.JAP是依赖于JDBC才能操作数据库的
JPA的对象四种状态
- 临时状态:指对象刚刚创建出来,还没有与entityManager发生关系,没有持久化,可以理解为还为与数据库进行交互
- 持久状态:与entityManager发生关系,操作了数据库,已经被持久化,比如:查询出来的数据,添加数据,修改的数据
- 删除状态:执行了remove方法但是还为提交事务
- 游离状态:数据提交到数据库,事务commit后,此时再对数据进行操作也不会同步到数据库
下面是JPA的一个官方文档,里面有一些协议用法,约定大于协议!!
Spring Data JPA - Reference Documentation
下面这个就是节选于官方文档的一部分,面向方法名
引入依赖
根据官方文档,找到现在较为稳定的版本
点开文档,找到依赖,2.6.1版本的依赖我会在下面给出,直接复制就好
将其复制到自己的pom文件中
这个依赖就帮我们管理所有相关的依赖版本,防止依赖冲突。
继续向下翻文档,找到jpa的依赖
这里是所需要的所有依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.32.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.10</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-bom</artifactId>
<version>2021.1.2</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
码代码
很多内容我会同时给出官方文档的位置以及粘贴出我自己代码,便于以后在用新的版本时也可以根据文档去找到相应的代码
实体类
package com.LL.pojo;
import lombok.Data;
import javax.persistence.*;
import java.lang.annotation.Target;
@Entity
@Table(name = "user")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "pwd")
private String pwd;
}
参照官方javaConfig文档,
完成我们的xml配置
下面是xml配置文件在官方文档中的位置
根据javaConfig文档完成spring.xml配置文件
<?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:tx="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--用于整合jpa-->
<jpa:repositories base-package="com.LL.repositories"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
<!--EntityManagerFactory-->
<bean name="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--hibernate实现-->
<property name="JpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--设置是否自动生成表-->
<!--value为true,若没有表则创建,为false,则不会创建-->
<property name="generateDdl" value="true"></property>
<!--显示sql语句-->
<property name="showSql" value="true"></property>
</bean>
</property>
<!--设置扫描实体类的包-->
<property name="packagesToScan" value="com.LL.pojo"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--数据源-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="123456789"></property>
</bean>
<!--声明式事务-->
<bean class="org.springframework.orm.jpa.JpaTransactionManager" name="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!--启动注解方式的声明式事务-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
建立一个新包,将xml的路径替换成新建包的路径
在repositories下建接口
package com.LL.repositories;
import com.LL.pojo.User;
import org.springframework.data.repository.CrudRepository;
//CrudRepository第一个参数是实体类型,第二个参数是主键类型
public interface UserRepository extends CrudRepository<User,Long> {
}
编写测试类
package com.LL;
import com.LL.pojo.User;
import com.LL.repositories.UserRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Optional;
//引入配置文件
@ContextConfiguration("/spring.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class jpa_Test {
@Autowired
UserRepository repository;
@Test
public void test1() {
//Optional防止空指针
Optional<User> user = repository.findById(1);
System.out.println(user.get());
}
@Test
public void test2() {
User user = new User();
user.setName("哈利路亚");
repository.save(user);
}
}
测试成功
javaConfig如何来配置
建立一个config类
直接将官方文档给的JAVAconfig配置类拿过来(拿来把你!),因为我这里用的是druid连接池,所以将DataSource做一点修改,改成自己的数据信息。将包扫描路径换成自己的路径
package com.LL.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
//标记当前类为配置类
@Configuration
//启动jpa <jpa:repositories
//如果当前的配置类不是在最顶层,那么我们需要指定
@EnableJpaRepositories(basePackages = "com.LL.repositories")
//开启事务
@EnableTransactionManagement
public class SpringDataJPAConfig {
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456789");
return druidDataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.acme.domain");
factory.setDataSource(dataSource());
return factory;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
CrudRepository中的方法说明
用来修改和插入 entity中有主键,则修改库中内容,如果没有主键,在会新增内容
返回结果为主键的值
<S extends T> S save(S entity);
通过集合返回保存多个实体
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
通过主键查询实体
Optional<T> findById(ID id);
通过id查询是否存在,返回布尔值
boolean existsById(ID id);
查询所有
Iterable<T> findAll();
通过集合的主键,查询多个实体,返回实体集合
Iterable<T> findAllById(Iterable<ID> ids);
返回总数量
long count();
根据id删除数据
void deleteById(ID id);
根据实体进行删除
void delete(T entity);
通过id集合删除多个
void deleteAllById(Iterable<? extends ID> ids);
通过实体,删除多个
void deleteAll(Iterable<? extends T> entities);
删除所有
void deleteAll();
分页与排序
若想增加分页和排序功能,则需要继承 PagingAndSortingRepository
PageRequest.of(0,2)第一个参数传的是查询第几页,第二个参数传的是每一页有多少的数据
@Test
public void test4(){
Page<User> all = repository.findAll(PageRequest.of(0, 2));
for (User user : all) {
System.out.println(user);
}
}
下面调用Page中的一些用法
@Test
public void test4(){
Page<User> all = repository.findAll(PageRequest.of(0, 2));
System.out.println("查询总页数"+all.getTotalPages());
System.out.println("查询总个数"+all.getTotalElements());
System.out.println("查询数据"+all.getContent());
}
结果输出正确
根据id进行降序排序(and方法表示可以定义多种排序方式,这里并无实意)
@Test
public void test5() {
Sort sort = Sort.by("id").descending().and(Sort.by("pwd").descending());
Iterable<User> all = repository.findAll(sort);
for (User user : all) {
System.out.println(user);
}
}
结果输出正确
自定义语句
我们可以自定义方法,首先我们看看如何进行传参。
@Query注解中可以指明sql方法,如果查询实体的所有信息,则select * 可以省略掉。
向@Query传参有两种方法
1.传参?+数字,数字指带第几个参数
2.使用@Param注解指明传参名称,传参:+名称
@Transactional注解声明事务,通常加载业务逻辑层上
@Modifying 通知springdatajpa是增删改的操作
//查询
//如果查询实体的所有字段,则select *可以省略,我们需要在?后面指明是第几个参数。
@Query("FROM User where name = ?1")
User findUserByName1( String userName);
@Query("FROM User where name =:s")
User findUserByName2(@Param("s") String userName);
//修改
@Transactional //放在业务逻辑层上面声明
@Modifying //通知springdatajpa是增删改的操作
@Query("update User u set u.pwd=:password where u.id =:id ")
int updateUser(@Param("password")String pass,@Param("id")int id);
//删除
@Transactional //放在业务逻辑层上面声明
@Modifying //通知springdatajpa是增删改的操作
@Query("delete from User u where u.id =:id ")
int deleteUser(@Param("id")int id);
源生方法
我们还可以自己去写sql语句进行处理。
nativeQuery默认是false,设置为true,使用源生语句。
@Query(value = "select * from user where id=?1"
,nativeQuery = true)
User findByIdBySql(@Param("id")int id);
测试方法
这里我使用了Optional去修饰实体类,可以防止空指针异常
@Test
public void test11(){
Optional<User> user = Optional.ofNullable(repository.findByIdBySql(5));
System.out.println(user);
}
常见规范命名方法
动态查询
1.Query by Example
1.只支持字符串 start/contains/ends/regex 匹配和其他属性配型的精准匹配
2.不支持嵌套或分组的属性约束
实例
新建一个Repository
public interface UserQBERepository
extends PagingAndSortingRepository<User,Integer>
, QueryByExampleExecutor<User> {
}
动态查询测试
新建一个User,设置名字为张三(假设这个数据是从前端传过来的),通过 Example构建查询条件,输出查询结果。
//动态查询
@Test
public void test1(){
//查询条件
User user = new User();
user.setName("张三");
//通过Example构建查询条件
Example<User> example = Example.of(user);
List<User> all = (List<User>) repository.findAll(example);
System.out.println(all);
}
我们还可以通过条件匹配器进行筛选
我们声明一个ExampleMatcher匹配器,帮助我们筛选数据,从筛选来看可以看到很多with开头的方法,第一个方法就是忽略掉这个参数,查询的时候不进行匹配,第二个参数就是忽略参数的大小写我们会在下面举例子。
下列代码中,我们查询一个名字叫张三,密码是1234的人,但实际中我们的库中并没有这个人,我们通过一个匹配器,选择忽略到名字和id,忽略密码的大小写,然后将匹配器传入到Example中,查询返回结果。
//条件匹配器
@Test
public void test2(){
//查询条件
User user = new User();
user.setName("张三");
user.setPwd("1234");
//通过匹配器 对条件进行蛇者
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("name") //设置忽略的值
.withIgnorePaths("id") //设置忽略的值
.withIgnoreCase("pwd"); //设置忽略大小写
Example<User> example = Example.of(user,matcher);
List<User> all = (List<User>) repository.findAll(example);
System.out.println(all);
}
下面是根据密码模糊查询的例子,查询出以4结尾的密码记录。
//条件匹配器
@Test
public void test3(){
//查询条件
User user = new User();
user.setPwd("4");
//通过匹配器 对条件进行蛇者
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("id") //设置忽略的值
.withIgnoreCase("pwd") //设置忽略大小写
//.withStringMatcher(ExampleMatcher.StringMatcher.ENDING); //根据结尾模糊查询
.withMatcher("pwd",m ->m.endsWith() ); //对单个参数有效,结尾模糊查询询
Example<User> example = Example.of(user,matcher);
List<User> all = (List<User>) repository.findAll(example);
System.out.println(all);
}
小结:
1.withIgnorePaths等with开头的方法可以帮助我们设置查询条件。
2.withStringMatcher方法是匹配我们实体中的所有数据,对所有属性都有效
3.withMatcher对单个参数有效
2.Specification
可以实现范围查询!
虽然代码看起来很复杂,但是实际上很好理解。
Specification中的三个参数
1.root 获取属性列
2.CriteriaBuilder 设置各种条件(大于,小于,在...范围内。。。。)
3.CriteriaQuery 组合
下列代码中,通过root去获取实体的三个属性参数,没有啥好解释的。。。
cb呢就是为我们的参数设置条件,比如
cb.equal(pwd, "1234");就是设置密码为1234
cb.greaterThan(id, 2);就是设置id大于2
cb.in(id);in.value(3).value(4).value(5);就是设置id的值可以是3、4、5
cb.and(equal,predicate, in);就是使这三个条件同时成立(and是同时成立,or是或者)
然后返回查询条件,查看查询结果。
@Test
public void test2() {
List<User> all = repository.findAll(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Integer> id = root.get("id");
Path<String> name = root.get("name");
Path<String> pwd = root.get("pwd");
//参数1:为那个字段设置条件 参数2:
Predicate equal = cb.equal(pwd, "1234");
Predicate predicate = cb.greaterThan(id, 2);
CriteriaBuilder.In<Integer> in = cb.in(id);
in.value(3).value(4).value(5);
Predicate and = cb.and(equal,predicate, in);
return and;
}
});
System.out.println(all);
}
3.QueryDSL查询框架
首先引入依赖
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--添加QueryDSL插件支持-->
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/queries</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
<logOnlyOnError>true</logOnlyOnError>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
代码示例,我们使用Ql类成功获取调用对象方法
@Test
public void test1() {
QUser user = QUser.user;
BooleanExpression eq = user.id.eq(12);
System.out.println(repository.findOne(eq));
}
下列查询的条件是,名字在张三、李四、王五中并且id大于1
/**
* 查询客户范围(in)
* id >大于
*/
@Test
public void test2() {
QUser user = QUser.user;
BooleanExpression and = user.name.in("张三", "李四", "王五")
.and(user.id.gt(1));
System.out.println(repository.findAll(and));
}
方法简写对应