Common ORM frameworks
Mybatis (ibatis): An excellent persistence layer framework that supports custom SQL, stored procedures, and advanced mapping. MyBatis avoids almost all JDBC code and manually setting parameters and getting result sets. MyBatis can use simple XML or annotations to configure and map native information, and map interfaces and Java POJOs (Plain Ordinary Java Objects, ordinary Java objects) into records in the database.
Hibernate: An open source object-relational mapping framework, which encapsulates JDBC with a very lightweight object, and establishes a mapping relationship between POJO and database tables. It is a fully automatic ORM framework. Hibernate can automatically generate SQL statements. Automatic execution allows Java programmers to use object programming thinking to manipulate the database as they wish.
Jpa: The abbreviation of Java Persistence API, the Chinese name is Java Persistence Layer API, which is JDK 5.0 annotation or XML description object-relational table mapping relationship, and persists the entity object in the runtime to the database.
1. Introduction to Spring data JPA
Spring data JPA is a set of JPA application framework encapsulated by Spring on the basis of ORM framework and JPA specification, and provides a complete set of data access layer solutions.
Second, the function of Spring data JPA
The function of Spring data JPA is very powerful, here we skip the step of setting up the environment first, to see the "beauty" of Spring data JPA.
Spring data JPA provides users with the following interfaces:
- Repository: It is just a logo, indicating that anyone who inherits it is a warehouse interface class, which is convenient for Spring to automatically scan and identify
- CrudRepository: Inherit Repository and implement a set of CRUD-related methods
- PagingAndSortingRepository: Inherit CrudRepository and implement a set of methods related to paging sorting
- JpaRepository: Inherit PagingAndSortingRepository to implement a set of JPA specification-related methods
- JpaSpecificationExecutor: It is special and does not belong to the Repository system. It implements a set of methods related to JPA Criteria query.
3. Spring Data JPA's support for transactions
By default, methods implemented by Spring Data JPA use transactions. The method for the query type is equivalent to @Transactional(readOnly=true); the method for adding, deleting and modifying the type is equivalent to @Transactional. It can be seen that, except for setting the query method as a read-only transaction, other transaction attributes adopt default values.
If the user finds it necessary, the transaction attribute can be explicitly specified using @Transactional on the interface method, which overrides the default value provided by Spring Data JPA. At the same time, developers can also use @Transactional to specify transaction attributes on the business layer method, which is mainly for the case where a business layer method calls the persistence layer method multiple times. The transaction of the persistence layer will decide whether to suspend the transaction of the business layer or join the transaction of the business layer according to the transaction propagation behavior set.
Four, JPA custom SQL
As the name suggests, based on the name of the method, you can create a query
As long as you inherit the JpaRepository<Object,Integer> interface, you can implement custom SQL. It is best to use this query for simple queries.
Keyword | Sample | JPQL snippet |
---|---|---|
And |
findByLastnameAndFirstname |
… where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
… where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals |
findByFirstname ,findByFirstnameIs ,findByFirstnameEquals |
… where x.firstname = ?1 |
Between |
findByStartDateBetween |
… where x.startDate between ?1 and ?2 |
LessThan |
findByAgeLessThan |
… where x.age < ?1 |
LessThanEqual |
findByAgeLessThanEqual |
… where x.age ⇐ ?1 |
GreaterThan |
findByAgeGreaterThan |
… where x.age > ?1 |
GreaterThanEqual |
findByAgeGreaterThanEqual |
… where x.age >= ?1 |
After |
findByStartDateAfter |
… where x.startDate > ?1 |
Before |
findByStartDateBefore |
… where x.startDate < ?1 |
IsNull |
findByAgeIsNull |
… where x.age is null |
IsNotNull,NotNull |
findByAge(Is)NotNull |
… where x.age not null |
Like |
findByFirstnameLike |
… where x.firstname like ?1 |
NotLike |
findByFirstnameNotLike |
… where x.firstname not like ?1 |
StartingWith |
findByFirstnameStartingWith |
… where x.firstname like ?1 (parameter bound with appended % ) |
EndingWith |
findByFirstnameEndingWith |
… where x.firstname like ?1 (parameter bound with prepended % ) |
Containing |
findByFirstnameContaining |
… where x.firstname like ?1 (parameter bound wrapped in% ) |
OrderBy |
findByAgeOrderByLastnameDesc |
… where x.age = ?1 order by x.lastname desc |
Not |
findByLastnameNot |
… where x.lastname <> ?1 |
In |
findByAgeIn(Collection<Age> ages) |
… where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection<Age> age) |
… where x.age not in ?1 |
True |
findByActiveTrue() |
… where x.active = true |
False |
findByActiveFalse() |
… where x.active = false |
IgnoreCase |
findByFirstnameIgnoreCase |
… where UPPER(x.firstame) = UPPER(?1) |
5. What annotations does JPA have?
Note: JPA can automatically generate tables based on entity class attributes, just add the following annotations
@Entity
@Table(name = “”)
@Column(name = “”)
annotation | explain |
---|---|
@Entity | Declare the class as an entity or a table. |
@Table | Declare the table name. |
@Id | Specifies the attribute of the class used for identification (primary key in a table). |
@GeneratedValue | Specifies how the ID property can be initialized, such as automatically, manually, or with a value obtained from a sequence table. |
@Column | Specifies the persistent property column properties. |
@Transient | Specifies a property that is not persistent, ie: the value is never stored in the database. |
@Basic | Specify individual fields that are not constrained explicitly. |
@Embedded | Specifies the class or attribute whose value is an instance of an embeddable class entity. |
@SequenceGenerator | Specifies the value of the property specified in the @GeneratedValue annotation. It creates a sequence. |
@TableGenerator | Specifies the value generator for the property specified in the @GeneratedValue annotation. It creates a generated table of values. |
@AccessType | This type of annotation is used to set the access type. If you set @AccessType(FIELD), you can access the variable directly and don't need getter and setter, but it must be public. If @AccessType(PROPERTY) is set, Entity variables are accessed through getter and setter methods. |
@JoinColumn | Specifies an entity organization or collection of entities. This is used in many-to-one and one-to-many associations. |
@UniqueConstraint | Specify fields and unique constraints for primary or secondary tables. |
@ColumnResult | Refer to column names in SQL queries using the select clause. |
@ManyToMany | Defines a many-to-many-to-many relationship between joined tables. |
@ManyToOne | Defines a many-to-one relationship between joined tables. |
@OneToMany | Defines a one-to-many relationship between the join tables. |
@OneToOne | There is a one-to-one relationship between the join tables defined. |
@NamedQueries | Specifies a list of named queries. |
@NamedQuery | Specifies a query using a static name. |
了解了注解之后我们来看看如何使用吧
六、SQL能力
1. JPA有两种SQL查询方式
因为接口继承了JpaRepository<Object,Integer>接口,所以接口可以实现里面的所有方法
JPA的查询语言是面向对象而非面向数据库的,他以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、Having 等通常只有SQL才能够提供的高级查询特性,甚至还能够支持子查询。
原生SQL
原生SQL,顾名思义就是数据库原原本本的SQL语句,可以把SQL语句复制出来到数据库直接运行
/**
* 获取用户信息
* nativeQuery = true:使用sql查询
* nativeQuery = false:使用jpql查询,默认就是false
*/
@Query(value = "select * from users where username=?", nativeQuery = true)
Users findByUsername(String username);
JPQL查询
JPQL查询使用的是实体类属性
@Query(value = "select u.id,u.name from users u where u.username=?")
Users findByUsername(String username);
注意:使用如下SQL需要定义实体类构造函数,并且参数顺序一致
@Query("select new com.example.demo.entity.TestDemo(t.id,t.username,t.testTime) from TestDemo t")
List<TestDemo> testAll();
2. 传参方式
下表索引传参
@Query ( "select u from users u where id>?1 and username like %?2%" )
List <User > selectUserByParam ( Long id , String name );
@param 命名参数传参
@Query ( "select u from users u where id>:id and username like :name" )
List <User > selectUserByParam ( @param('id') Long id , @param('name') String name );
对象传参
@Query ( "select u from users u where id>:#{#users.id} and username like :#{#users.name}" )
List <User> selectUserByParam2 (Users users);
3. 动态SQL
是不是跟Mybatis的动态SQL有点相识,只不过这里是判断是否为空,为空了就不会往下走了,不为空就执行SQL将参数传进去进行查询
@Query("select t from TestDemo t where ((?1 is null or ?1 = '') or (?2 is null or ?2 = '') or (t.testTime between ?1 and ?2)) ")
Page<TestDemo> testAll1(String startTime, String endTime, Pageable pageable);
4. 两表联合分页查询
注意JPA原生SQL和Pageable配合会报错
JPA原生SQL和Pageable配合报错解决:https://blog.csdn.net/ITKidKid/article/details/130167244
这里只是做个示例,随便写的,具体根据实际情况开发
数据访问层 testRepository 接口
@Query("select new com.example.demo.vo.UserAddressVo(u.id,a.province,a.city,a.area) from User u inner join Address a on u.id = a.userId")
Page<UserAddressVo> pageAll(Pageable pageable);
业务逻辑层 testServiceImpl 接口实现类
testRepository.pageAll(PageRequest.of(page - 1, limit, Sort.Direction.DESC, "createDate"));
七、项目实战
1. 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>compile</scope>
</dependency>
2. 生成代码
JPA代码生成:https://www.cnblogs.com/cl-rr/p/10397107.html
可以使用eazycode代码生成器生成代码,也可以使用我上面的这个链接,不过有些地方需要补全
3. 代码编写
控制层
/**
* (TestDemo)表控制层
*
* @author makejava
* @since 2023-04-26 14:32:35
*/
@RestController
@RequestMapping("/testDemo")
public class TestDemoController {
@GetMapping("/testAll")
public RespBean testAll(@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime) {
return RespBean.success("成功",testDemoService.testAll(startTime,endTime));
}
}
业务逻辑层
接口方法
public interface TestDemoService {
List<TestDemo> testAll(String startTime, String endTime);
}
接口方法实现
@Service
public class TestDemoServiceImpl implements TestDemoService {
@Autowired
private TestDemoRepository rep;
@Override
public List<TestDemo> testAll(String startTime, String endTime) {
return rep.testAll(startTime,endTime);
}
}
业务访问层
@Repository
public interface TestDemoRepository extends JpaRepository<TestDemo, Integer> {
@Query("select new com.example.demo.entity.TestDemo(t.id,t.username,t.testTime) from TestDemo t where ((?1 is null or ?1 = '') or (?2 is null or ?2 = '') or (t.testTime between ?1 and ?2)) ")
List<TestDemo> testAll(String startTime, String endTime);
}
看下结果:
Java全局日期类型转换配置:https://blog.csdn.net/ITKidKid/article/details/130421992
感谢:https://blog.csdn.net/wujiaqi0921/article/details/78789087