hql实例 jpa_SpringBoot学习笔记九:Spring Data Jpa的使用

Spring Data Jpa 简介

JPA

JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR 338,这些接口所在包为javax.persistence,详细内容可参考https://github.com/javaee/jpa-spec)

JPA的出现主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。总的来说,JPA包括以下3方面的技术:

ORM映射元数据: 支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系

API: 操作实体对象来执行CRUD操作

查询语言: 通过面向对象而非面向数据库的查询语言(JPQL)查询数据,避免程序的SQL语句紧密耦合

JPA架构

Spring Data Jpa

Spring Data Jpa官方解释

Spring Data JPA是Spring Data家族的一部分,可以轻松实现基于JPA的存储库。 此模块处理对基于JPA的数据访问层的增强支持。 它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。

在相当长的一段时间内,实现应用程序的数据访问层一直很麻烦。 必须编写太多样板代码来执行简单查询以及执行分页和审计。 Spring Data JPA旨在通过减少实际需要的工作量来显著改善数据访问层的实现。 作为开发人员,您编写repository接口,包括自定义查找器方法,Spring将自动提供实现。

Spring Data生态

Jpa、Hibernate、Spring Data Jpa三者之间的关系

总的来说JPA是ORM规范,Hibernate、TopLink等是JPA规范的具体实现,这样的好处是开发者可以面向JPA规范进行持久层的开发,而底层的实现则是可以切换的。Spring Data Jpa则是在JPA之上添加另一层抽象(Repository层的实现),极大地简化持久层开发及ORM框架切换的成本。

Jpa、Hibernate、Spring Data Jpa三者之间的关系

Spring Data Jpa的java配置方案

在Spring Boot没出来之前如果要采用Java Configuration来配置Spring Data Jpa你需要配置如下的Bean

参考自Spring In Action及Spring Data Jpa官方文档5.1.2. Annotation-based Configuration

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.JpaVendorAdapter;

import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

import org.springframework.orm.jpa.vendor.Database;

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;

/**

* 注意:spring-data-jpa2.x版本需要spring版本为5.x

* 否则会报Initialization of bean failed; nested exception is java.lang.AbstractMethodError错误

* 参考:https://stackoverflow.com/questions/47558017/error-starting-a-spring-application-initialization-of-bean-failed-nested-excep

* 搭配方案:spring4+spring-data-jpa1.x或spring5+spring-data-jpa2.x

*/

@Configuration

// 借助spring data实现自动化的jpa repository,只需编写接口无需编写实现类

// 相当于xml配置的

// repositoryImplementationPostfix默认就是Impl

// entityManagerFactoryRef默认就是entityManagerFactory

// transactionManagerRef默认就是transactionManager

@EnableJpaRepositories(basePackages = {"com.example.repository"},

repositoryImplementationPostfix = "Impl",

entityManagerFactoryRef = "entityManagerFactory",

transactionManagerRef = "transactionManager")

@EnableTransactionManagement // 启用事务管理器

public class SpringDataJpaConfig {

// 配置jpa厂商适配器(参见spring实战p320)

@Bean

public JpaVendorAdapter jpaVendorAdapter() {

HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();

// 设置数据库类型(可使用org.springframework.orm.jpa.vendor包下的Database枚举类)

jpaVendorAdapter.setDatabase(Database.MYSQL);

// 设置打印sql语句

jpaVendorAdapter.setShowSql(true);

// 设置不生成ddl语句

jpaVendorAdapter.setGenerateDdl(false);

// 设置hibernate方言

jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect");

return jpaVendorAdapter;

}

// 配置实体管理器工厂

@Bean

public LocalContainerEntityManagerFactoryBean entityManagerFactory(

DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {

LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();

// 注入数据源

emfb.setDataSource(dataSource);

// 注入jpa厂商适配器

emfb.setJpaVendorAdapter(jpaVendorAdapter);

// 设置扫描基本包

emfb.setPackagesToScan("com.example.entity");

return emfb;

}

// 配置jpa事务管理器

@Bean

public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {

JpaTransactionManager transactionManager = new JpaTransactionManager();

// 配置实体管理器工厂

transactionManager.setEntityManagerFactory(emf);

return transactionManager;

}

}

启用web支持还需要在Spring MVC配置类上添加@EnableSpringDataWebSupport注解

@Configuration

@ComponentScan(basePackages = {"cn.fulgens.controller"})

@EnableWebMvc // 启用spring mvc

@EnableSpringDataWebSupport // 启用springmvc对spring data的支持

public class WebMvcConfig extends WebMvcConfigurerAdapter {

}

Spring Boot整合Spring Data Jpa

导入依赖

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-data-jpa

mysql

mysql-connector-java

runtime

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-devtools

true

org.springframework.boot

spring-boot-starter-test

test

相关配置

server:

port: 8080

servlet:

context-path: /

spring:

datasource:

url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false

username: root

password: mysql123

jpa:

database: MySQL

database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

show-sql: true

hibernate:

ddl-auto: update

ddl-auto

create:每次运行程序时,都会重新创建表,故而数据会丢失

create-drop:每次运行程序时会先创建表结构,然后待程序结束时清空表

upadte:每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新(推荐使用)

validate:运行程序会校验数据与数据库的字段类型是否相同,字段不同会报错

none: 禁用DDL处理

注意:

Spring Data Jpa的使用

Spring Data Jpa UML类图

Spring Data Jpa UML

简单的REST CRUD示例

实体类

/src/main/java/com/example/springbootjpa/entity/User

package com.example.springbootjpa.entity;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

@Entity

@Table(name = "tb_user")

@Data

public class User {

@Id

@GenericGenerator(name = "idGenerator", strategy = "uuid")

@GeneratedValue(generator = "idGenerator")

private String id;

@Column(name = "username", unique = true, nullable = false, length = 64)

private String username;

@Column(name = "password", nullable = false, length = 64)

private String password;

@Column(name = "email", length = 64)

private String email;

}

主键采用UUID策略

@GenericGenerator是Hibernate提供的主键生成策略注解,注意下面的@GeneratedValue(JPA注解)使用generator = "idGenerator"引用了上面的name = "idGenerator"主键生成策略

一般简单的Demo示例中只会使用@GeneratedValue(strategy = GenerationType.IDENTITY)这种主键自增的策略,而实际数据库中表字段主键类型很少是int型的

JPA自带的几种主键生成策略

TABLE: 使用一个特定的数据库表格来保存主键

SEQUENCE: 根据底层数据库的序列来生成主键,条件是数据库支持序列。这个值要与generator一起使用,generator 指定生成主键使用的生成器(可能是orcale中自己编写的序列)

IDENTITY: 主键由数据库自动生成(主要是支持自动增长的数据库,如mysql)

AUTO: 主键由程序控制,也是GenerationType的默认值

Dao层

/src/main/java/com/example/springbootjpa/repository/UserRepository

package com.example.springbootjpa.repository;

import com.example.springbootjpa.entity.User;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository {

}

Controller层

这里简单起见省略Service层

/src/main/java/com/example/springbootjpa/controller/UserController

package com.example.springbootjpa.controller;

import com.example.springbootjpa.entity.User;

import com.example.springbootjpa.repository.UserRepository;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Page;

import org.springframework.data.domain.PageRequest;

import org.springframework.data.domain.Pageable;

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

import java.util.Optional;

@RestController

@RequestMapping("/users")

public class UserController {

@Autowired

private UserRepository userRepository;

@PostMapping()

public User saveUser(@RequestBody User user) {

return userRepository.save(user);

}

@DeleteMapping("/{id}")

public void deleteUser(@PathVariable("id") String userId) {

userRepository.deleteById(userId);

}

@PutMapping("/{id}")

public User updateUser(@PathVariable("id") String userId, @RequestBody User user) {

user.setId(userId);

return userRepository.saveAndFlush(user);

}

@GetMapping("/{id}")

public User getUserInfo(@PathVariable("id") String userId) {

Optional optional = userRepository.findById(userId);

return optional.orElseGet(User::new);

}

@GetMapping("/list")

public Page pageQuery(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,

@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {

return userRepository.findAll(PageRequest.of(pageNum - 1, pageSize));

}

}

Spring Data Jpa使用详解

Spring Data查询方法

使用Spring Data创建查询只需四步:

声明一个接口继承自Repository或Repositoy的一个子接口,对于Spring Data Jpa通常是JpaRepository,如:

interface PersonRepository extends Repository { … }

在接口中声明查询方法,如:

interface PersonRepository extends Repository {

List findByLastname(String lastname);

}

使用 JavaConfig 或 XML configuration配置Spring,让 Spring 为声明的接口创建代理对象

3.1 JavaConfig参见上文

3.2 使用Xml配置,可以像下面这样使用jpa命名空间进行配置:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:jpa="http://www.springframework.org/schema/data/jpa"

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">

顺带一提,对于不同的Spring Data子项目Spring提供了不同的xml命名空间,如对于Spring Data MongoDB可以将上面的jpa改为mongodb

当然,使用Spring Boot这一步基本可以省略,我们需要做的就是在application.properties或application.yml文件中配置几个属性即可

注入Repository实例并使用,如:

class SomeClient {

private final PersonRepository repository;

SomeClient(PersonRepository repository) {

this.repository = repository;

}

void doSomething() {

List persons = repository.findByLastname("Matthews");

}

}

定义Repository接口

选择性暴露CRUD方法

一种方法是定义一个BaseRepository接口继承Repository接口,并从CrudRepository中copy你想暴露的CRUD方法

src/main/java/com/example/springbootjpa/repository/MyBaseRepository

package com.example.springbootjpa.repository;

import org.springframework.data.repository.NoRepositoryBean;

import org.springframework.data.repository.Repository;

import java.util.Optional;

/**

* 自定义Repository,选择性暴露CRUD方法

* @param

* @param

*/

@NoRepositoryBean

public interface MyBaseRepository extends Repository {

Optional findById(ID id);

S save(S entity);

}

注意:MyBaseRepository上面加了@NoRepositoryBean注解

src/main/java/com/example/springbootjpa/repository/UserRepository2

package com.example.springbootjpa.repository;

import com.example.springbootjpa.entity.User;

import org.springframework.stereotype.Repository;

public interface UserRepository2 extends MyBaseRepository {

}

Junit测试

package com.example.springbootjpa.repository;

import org.junit.Assert;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit4.SpringRunner;

import java.util.Optional;

@RunWith(SpringRunner.class)

@SpringBootTest

public class UserRepository2Test {

@Autowired

private UserRepository2 userRepositoy;

@Test

public void findByIdTest() {

Optional optional = userRepositoy.findById("40289f0c65674a930165674d54940000");

Assert.assertNotNull(optional.get());

}

}

这里启动Junit测试时报了一个错,记录一下

java.lang.IllegalStateException: Failed to load ApplicationContext

...

Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException

at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)

at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)

at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)

... 50 more

错误很明显Spring应用上下文加载失败,原因是找不到javax.xml.bind.JAXBException

手贱从java8升级到java10,JAXB API是java EE 的API,在java SE 9.0 中已经不再包含这个 Jar 包。java9 中引入了模块的概念,默认情况下,Java SE中将不再包含java EE 的Jar包,而在 java 6/7 / 8 时关于这个API 都是捆绑在一起的,解决方法添加如下jar包

javax.xml.bind

jaxb-api

2.3.0

com.sun.xml.bind

jaxb-impl

2.3.0

com.sun.xml.bind

jaxb-core

2.3.0

javax.activation

activation

1.1.1

另一种方法是使用@RepositoryDefinition注解,并从CrudRepository中copy你想暴露的CRUD方法

src/main/java/com/example/springbootjpa/repository/UserRepository3

package com.example.springbootjpa.repository;

import com.example.springbootjpa.entity.User;

import org.springframework.data.repository.RepositoryDefinition;

import java.util.Optional;

@RepositoryDefinition(domainClass = User.class, idClass = String.class)

public interface UserRepository3 {

Optional findById(String id);

User save(User user);

}

Repository方法的Null值处理

从Spring Data2.0开始对于返回单个聚合实例的CRUD方法可以使用java8 Optional接口作为方法返回值来表明可能存在的缺省值,典型示例为CrudRepository的findById方法

另外Spring也提供了几个注解来处理Null值

@NonNullApi: 在包级别使用来声明参数和返回值不能为Null

@NonNull: 在参数或返回值上使用,当它们不能为Null时(如果在包级别上使用了@NonNullApi注解则没有必要再使用@NonNull注解了)

@Nullable: 在参数或返回值上使用,当它们可以为Null时

查询方法

查询创建Query Creation

Spring Data Jpa通过解析方法名创建查询,框架在进行方法名解析时,会先把方法名多余的前缀find…By, read…By, query…By, count…By以及get…By截取掉,然后对剩下部分进行解析,第一个By会被用作分隔符来指示实际查询条件的开始。 我们可以在实体属性上定义条件,并将它们与And和Or连接起来,从而创建大量查询:

User findByUsername(String username);

List findByUsernameIgnoreCase(String username);

List findByUsernameLike(String username);

User findByUsernameAndPassword(String username, String password);

User findByEmail(String email);

List findByEmailLike(String email);

List findByIdIn(List ids);

List findByIdInOrderByUsername(List ids);

void deleteByIdIn(List ids);

Long countByUsernameLike(String username);

支持的关键字、示例及JPQL片段如下表所示:

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

... findByFirstnameNotLike

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 ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection ages)

… 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)

限制查询结果

Spring Data Jpa支持使用first、top以及Distinct 关键字来限制查询结果,如:

User findFirstByUsernameOrderByUsernameAsc(String username);

List findTop10ByUsername(String username, Sort sort);

List findTop10ByUsername(String username, Pageable pageable);

自定义查询Using @Query

@Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JPQL 查询语句即可

@Query("select u from User u where u.email = ?1")

User getByEmail(String eamil);

@Query("select u from User u where u.username = ?1 and u.password = ?2")

User getByUsernameAndPassword(String username, String password);

@Query("select u from User u where u.username like %?1%")

List getByUsernameLike(String username);

使用命名参数Using Named Parameters

默认情况下,Spring Data JPA使用基于位置的参数绑定,如前面所有示例中所述。 这使得查询方法在重构参数位置时容易出错。 要解决此问题,可以使用@Param注解为方法参数指定具体名称并在查询中绑定名称,如以下示例所示:

@Query("select u from User u where u.id = :id")

User getById(@Param("id") String userId);

@Query("select u from User u where u.username = :username or u.email = :email")

User getByUsernameOrEmail(@Param("username") String username, @Param("email") String email);

Using SpEL Expressions

从Spring Data JPA release 1.4开始,Spring Data JPA支持名为entityName的变量。 它的用法是select x from #{#entityName} x。 entityName的解析方式如下:如果实体类在@Entity注解上设置了name属性,则使用它。 否则,使用实体类的简单类名。为避免在@Query注解使用实际的实体类名,就可以使用#{#entityName}进行代替。如以上示例中,@Query注解的查询字符串里的User都可替换为#{#entityName}

@Query("select u from #{#entityName} u where u.email = ?1")

User getByEmail(String eamil);

原生查询Native Queries

@Query注解还支持通过将nativeQuery标志设置为true来执行原生查询,同样支持基于位置的参数绑定及命名参数,如:

@Query(value = "select * from tb_user u where u.email = ?1", nativeQuery = true)

User queryByEmail(String email);

@Query(value = "select * from tb_user u where u.email = :email", nativeQuery = true)

User queryByEmail(@Param("email") String email);

注意:Spring Data Jpa目前不支持对原生查询进行动态排序,但可以通过自己指定计数查询countQuery来使用原生查询进行分页、排序,如:

@Query(value = "select * from tb_user u where u.username like %?1%",

countQuery = "select count(1) from tb_user u where u.username = %?1%",

nativeQuery = true)

Page queryByUsernameLike(String username, Pageable pageable);

分页查询及排序

Spring Data Jpa可以在方法参数中直接传入Pageable或Sort来完成动态分页或排序,通常Pageable或Sort会是方法的最后一个参数,如:

@Query("select u from User u where u.username like %?1%")

Page findByUsernameLike(String username, Pageable pageable);

@Query("select u from User u where u.username like %?1%")

List findByUsernameAndSort(String username, Sort sort);

那调用repository方法时传入什么参数呢?

对于Pageable参数,在Spring Data 2.0之前我们可以new一个org.springframework.data.domain.PageRequest对象,现在这些构造方法已经废弃,取而代之Spring推荐我们使用PageRequest的of方法

new PageRequest(0, 5);

new PageRequest(0, 5, Sort.Direction.ASC, "username");

new PageRequest(0, 5, new Sort(Sort.Direction.ASC, "username"));

PageRequest.of(0, 5);

PageRequest.of(0, 5, Sort.Direction.ASC, "username");

PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "username"));

注意:Spring Data PageRequest的page参数是从0开始的 zero-based page index

对于Sort参数,同样可以new一个org.springframework.data.domain.Sort,但推荐使用Sort.by方法

自定义修改、删除 Modifying Queries

单独使用@Query注解只是查询,如涉及到修改、删除则需要再加上@Modifying注解,如:

@Transactional()

@Modifying

@Query("update User u set u.password = ?2 where u.username = ?1")

int updatePasswordByUsername(String username, String password);

@Transactional()

@Modifying

@Query("delete from User where username = ?1")

void deleteByUsername(String username);

注意:Modifying queries can only use void or int/Integer as return type!

多表查询

这里使用级联查询进行多表的关联查询

多对多

/src/main/java/com/example/springbootjpa/entity/User

package com.example.springbootjpa.entity;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

import java.util.Date;

import java.util.Set;

import java.util.UUID;

@Entity

@Table(name = "tb_user")

@Data

public class User {

@Id

@GenericGenerator(name = "idGenerator", strategy = "uuid")

@GeneratedValue(generator = "idGenerator")

private String id;

@Column(name = "username", unique = true, nullable = false, length = 64)

private String username;

@Column(name = "password", nullable = false, length = 64)

private String password;

@Column(name = "email", unique = true, length = 64)

private String email;

@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)

@JoinTable(name = "tb_user_role", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},

inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})

private Set roles;

}

/src/main/java/com/example/springbootjpa/entity/Role

package com.example.springbootjpa.entity;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

@Entity

@Table(name = "tb_role")

@Data

public class Role {

@Id

@GenericGenerator(name = "idGenerator", strategy = "uuid")

@GeneratedValue(generator = "idGenerator")

private String id;

@Column(name = "role_name", unique = true, nullable = false, length = 64)

private String roleName;

}

测试

@Test

public void findByIdTest() {

Optional optional = userRepository.findById("40289f0c65674a930165674d54940000");

Set roles = optional.get().getRoles();

System.out.println(optional.get());

}

不出意外会报Hibernate懒加载异常,无法初始化代理类,No Session:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.springbootjpa.entity.User.roles, could not initialize proxy - no Session

原因:Spring Boot整合JPA后Hibernate的Session就交付给Spring去管理。每次数据库操作后,会关闭Session,当我们想要用懒加载方式去获得数据的时候,原来的Session已经关闭,不能获取数据,所以会抛出这样的异常。

解决方法:

在application.yml中做如下配置:

spring:

jpa:

open-in-view: true

properties:

hibernate:

enable_lazy_load_no_trans: true

一对多(多对一)

/src/main/java/com/example/springbootjpa/entity/Department

package com.example.springbootjpa.entity;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

import java.util.Set;

@Entity

@Table(name = "tb_dept")

@Data

public class Department {

@Id

@GenericGenerator(name = "idGenerator", strategy = "uuid")

@GeneratedValue(generator = "idGenerator")

private String id;

@Column(name = "dept_name", unique = true, nullable = false, length = 64)

private String deptName;

@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, fetch = FetchType.LAZY)

private Set employees;

}

/src/main/java/com/example/springbootjpa/entity/Employee

package com.example.springbootjpa.entity;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

import java.util.UUID;

@Entity

@Table(name = "tb_emp")

@Data

public class Employee {

@Id

@GenericGenerator(name = "idGenerator", strategy = "uuid")

@GeneratedValue(generator = "idGenerator")

private String id;

@Column(name = "emp_name", nullable = false, length = 64)

private String empName;

@Column(name = "emp_job", length = 64)

private String empJob;

@Column(name = "dept_id", insertable = false, updatable = false)

private String deptId;

@ManyToOne(targetEntity = Department.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)

@JoinColumn(name = "dept_id")

private Department department;

}

测试

@Test

public void findByIdTest() {

Optional optional = employeeRepository.findById("93fce66c1ef340fa866d5bd389de3d79");

System.out.println(optional.get());

}

结果报错了...

java.lang.StackOverflowError

at java.base/java.lang.Exception.(Exception.java:102)

at java.base/java.lang.ReflectiveOperationException.(ReflectiveOperationException.java:89)

at java.base/java.lang.reflect.InvocationTargetException.(InvocationTargetException.java:73)

at jdk.internal.reflect.GeneratedConstructorAccessor54.newInstance(Unknown Source)

at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)

at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)

at com.mysql.jdbc.PreparedStatement.getInstance(PreparedStatement.java:761)

at com.mysql.jdbc.ConnectionImpl.clientPrepareStatement(ConnectionImpl.java:1404)

at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4121)

at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4025)

at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:318)

at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:145)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:171)

at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:147)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.prepareQueryStatement(AbstractLoadPlanBasedLoader.java:226)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:190)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:121)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:86)

at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87)

at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:688)

at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:75)

at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2223)

at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:565)

at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:247)

at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561)

at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)

at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:430)

at com.example.springbootjpa.entity.Department.hashCode(Department.java:14)

通过日志看sql的输出,发现了sql重复执行了好多次。以下我截取了前10条sql记录。

Hibernate: select employee0_.id as id1_1_0_, employee0_.dept_id as dept_id2_1_0_, employee0_.emp_job as emp_job3_1_0_, employee0_.emp_name as emp_name4_1_0_ from tb_emp employee0_ where employee0_.id=?

Hibernate: select department0_.id as id1_0_0_, department0_.dept_name as dept_nam2_0_0_ from tb_dept department0_ where department0_.id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

Hibernate: select department0_.id as id1_0_0_, department0_.dept_name as dept_nam2_0_0_ from tb_dept department0_ where department0_.id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

Hibernate: select employees0_.dept_id as dept_id2_1_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id2_1_1_, employees0_.emp_job as emp_job3_1_1_, employees0_.emp_name as emp_name4_1_1_ from tb_emp employees0_ where employees0_.dept_id=?

通过观察发现,第一条sql是执行查询Employee的sql,第二条sql是执行查询Department的sql,第三条sql是执行Department里面所有员工的sql,第四条sql是执行查询Department的sql,后面所有的sql都是执行查询Department里面所有员工的sql。

很明显发生了循环依赖的情况。这是Lombok的@Data注解的锅。Lombok的@Data注解相当于@Getter、@Setter、@RequiredArgsConstructor、@ToString、@EqualsAndHashCode这几个注解。

我们可以通过反编译看一下Lombok生成的toString()方法

// Employee

public String toString() {

return "Employee(id=" + getId() + ", empName=" + getEmpName() + ", empJob=" + getEmpJob() + ", deptId=" + getDeptId() + ", department=" + getDepartment() + ")";

}

// Department

public String toString() {

return "Department(id=" + getId() + ", deptName=" + getDeptName() + ", employees=" + getEmployees() + ")";

}

可以发现Lombok为我们生成的toString()方法覆盖了整个类的所有属性

现在将@Data注解去掉,替换为@Setter、@Getter、@EqualsAndHashCode,重写toString()方法

// Department

@Override

public String toString() {

return "Department{" +

"id='" + id + '\'' +

", deptName='" + deptName + '\'' +

'}';

}

// Employee

@Override

public String toString() {

return "Employee{" +

"id='" + id + '\'' +

", empName='" + empName + '\'' +

", empJob='" + empJob + '\'' +

", deptId='" + deptId + '\'' +

", department=" + department +

'}';

}

再次运行测试用例,测试通过,以上Employee toString()方法打印的department会触发懒加载,最终日志输出的sql如下:

Hibernate: select employee0_.id as id1_1_0_, employee0_.dept_id as dept_id2_1_0_, employee0_.emp_job as emp_job3_1_0_, employee0_.emp_name as emp_name4_1_0_ from tb_emp employee0_ where employee0_.id=?

Hibernate: select department0_.id as id1_0_0_, department0_.dept_name as dept_nam2_0_0_ from tb_dept department0_ where department0_.id=?

Employee{id='93fce66c1ef340fa866d5bd389de3d79', empName='jack', empJob='hr', deptId='0a4fe7234fff42afad34f6a06a8e1821', department=Department{id='0a4fe7234fff42afad34f6a06a8e1821', deptName='人事部'}}

再来测试查询Department

@Test

public void findByIdTest() {

Optional optional = departmentRepository.findById("0a4fe7234fff42afad34f6a06a8e1821");

Set employees = optional.get().getEmployees();

Assert.assertNotEquals(0, employees.size());

}

同样还是报了堆栈溢出,错误定位在Department和Employee的hashCode()方法上

java.lang.StackOverflowError

at com.mysql.jdbc.Util.handleNewInstance(Util.java:439)

at com.mysql.jdbc.ResultSetImpl.getInstance(ResultSetImpl.java:342)

at com.mysql.jdbc.MysqlIO.buildResultSetWithRows(MysqlIO.java:3132)

at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:477)

at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3115)

at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2344)

at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2739)

at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2486)

at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858)

at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1966)

at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)

at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)

at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.getResultSet(AbstractLoadPlanBasedLoader.java:419)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:191)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:121)

at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:86)

at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87)

at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:688)

at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:75)

at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2223)

at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:565)

at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:247)

at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561)

at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)

at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:430)

at com.example.springbootjpa.entity.Department.hashCode(Department.java:17)

依旧是Lombok的锅,@EqualsAndHashCode为我们生成的equals()和hashCode()方法会使用所有属性,注意,Department中employees是Set集合,当我们调用department.getEmployees()时,Employee的hashCode()方法会被调用,Employee中的hashCode()又依赖于Department的HashCode()方法,这样又形成了循环引用...

// Department

public int hashCode() {

int i = 43;

String $id = getId();

int result = ($id == null ? 43 : $id.hashCode()) + 59;

String $deptName = getDeptName();

result = (result * 59) + ($deptName == null ? 43 : $deptName.hashCode());

Set $employees = getEmployees();

int i2 = result * 59;

if ($employees != null) {

i = $employees.hashCode();

}

return i2 + i;

}

// Employee

public int hashCode() {

int i = 43;

String $id = getId();

int result = ($id == null ? 43 : $id.hashCode()) + 59;

String $empName = getEmpName();

result = (result * 59) + ($empName == null ? 43 : $empName.hashCode());

String $empJob = getEmpJob();

result = (result * 59) + ($empJob == null ? 43 : $empJob.hashCode());

String $deptId = getDeptId();

result = (result * 59) + ($deptId == null ? 43 : $deptId.hashCode());

Department $department = getDepartment();

int i2 = result * 59;

if ($department != null) {

i = $department.hashCode();

}

return i2 + i;

}

自己动手重写equals()和hashCode()方法,去掉@EqualsAndHashCode注解

// Department

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Department that = (Department) o;

return Objects.equals(id, that.id) &&

Objects.equals(deptName, that.deptName);

}

@Override

public int hashCode() {

return Objects.hash(id, deptName);

}

// Employee

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Employee employee = (Employee) o;

return Objects.equals(id, employee.id) &&

Objects.equals(empName, employee.empName) &&

Objects.equals(empJob, employee.empJob) &&

Objects.equals(deptId, employee.deptId);

}

@Override

public int hashCode() {

return Objects.hash(id, empName, empJob, deptId);

}

再次运行测试用例,测试通过

总结:慎用@Data注解,使用@Getter、@Setter注解,需要时自己重写toString()、equals()以及hashCode()方法

审计Auditing

参考自官方文档5.9Auditing

一般数据库表在设计时都会添加4个审计字段,Spring Data Jpa同样支持审计功能。Spring Data提供了@CreatedBy,@LastModifiedBy,@CreatedDate,@LastModifiedDate4个注解来记录表中记录的创建及修改信息。

实体类

package com.example.springbootjpa.entity;

import lombok.Data;

import org.hibernate.annotations.GenericGenerator;

import org.springframework.data.annotation.CreatedBy;

import org.springframework.data.annotation.CreatedDate;

import org.springframework.data.annotation.LastModifiedBy;

import org.springframework.data.annotation.LastModifiedDate;

import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;

import java.util.Date;

import java.util.Set;

@Entity

@EntityListeners(AuditingEntityListener.class)

@Table(name = "tb_user")

@Data

public class User {

@Id

@GenericGenerator(name = "idGenerator", strategy = "uuid")

@GeneratedValue(generator = "idGenerator")

private String id;

@Column(name = "username", unique = true, nullable = false, length = 64)

private String username;

@Column(name = "password", nullable = false, length = 64)

private String password;

@Column(name = "email", unique = true, length = 64)

private String email;

@ManyToMany(targetEntity = Role.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)

@JoinTable(name = "tb_user_role", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},

inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})

private Set roles;

@CreatedDate

@Column(name = "created_date", updatable = false)

private Date createdDate;

@CreatedBy

@Column(name = "created_by", updatable = false, length = 64)

private String createdBy;

@LastModifiedDate

@Column(name = "updated_date")

private Date updatedDate;

@LastModifiedBy

@Column(name = "updated_by", length = 64)

private String updatedBy;

}

实体类上还添加了@EntityListeners(AuditingEntityListener.class),而AuditingEntityListener是由Spring Data Jpa提供的

实现AuditorAware接口

光添加了4个审计注解还不够,得告诉程序到底是谁在创建和修改表记录

/src/main/java/com/example/springbootjpa/auditing/AuditorAwareImpl

package com.example.springbootjpa.auditing;

import org.springframework.data.domain.AuditorAware;

import org.springframework.stereotype.Component;

import java.util.Optional;

@Component

public class AuditorAwareImpl implements AuditorAware {

@Override

public Optional getCurrentAuditor() {

return Optional.of("admin");

}

}

这里简单的返回了一个"admin"字符串来代表当前用户名

启用Jpa审计功能

在Spring Boot启动类上添加@EnableJpaAuditing注解用于启用Jpa的审计功能

package com.example.springbootjpa;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication

@EnableJpaAuditing

public class SpringBootJpaApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBootJpaApplication.class, args);

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555625.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Objects.requireNonNull 方法说明

在写代码的时候,Idea经常会提醒我们可以使用这个方法来进行参数非空检查, 这个方法的源码也非常简单, 如下所示: /*** Checks that the specified object reference is not {code null}. This* method is designed primarily for doing parameter validation in methods* and …

Java service层获取HttpServletRequest工具类的方法

大家都知道 能在Controller/action层获取HttpServletRequest &#xff0c;但是这里给大家备份的是从代码内部service层获取HttpServletRequest工具类。 具体如下&#xff1a; package com.base.common.sessionutils; import javax.servlet.http.HttpServletRequest; import j…

正则表达式发明者_浅谈正则表达式背后的基本原理

一、写在前面搞编程的都知道正则表达式是什么东西&#xff0c;这里就不多啰嗦了&#xff0c;需要强调的是&#xff0c;这篇文章并不是教你怎么去使用用正则表达式&#xff0c;正则表达式的语法并不是本文的重点&#xff0c;这篇文章的目的就是剥开正则表达式的语法糖&#xff0…

Java8 - 使用 Comparator.comparing 进行排序

使用外部比较器Comparator进行排序 当我们需要对集合的元素进行排序的时候&#xff0c;可以使用java.util.Comparator 创建一个比较器来进行排序。Comparator接口同样也是一个函数式接口&#xff0c;我们可以把使用lambda表达式。如下示例&#xff0c; package com.common;im…

cairo填充_Cairo 图形指南 (5) —— 形状与填充

这一部分&#xff0c;讲述一些基本的以及较为高级的形状绘制及其纯色 (solid color)、图案 (pattern) 与渐变 (gradient) 填充方法。基本形状Cairo 提供了几个用于绘制基本形状的函数。#include#include#includestatic gbooleanon_expose_event (GtkWidget * widget,GdkEventEx…

java集合进行排序的两种方式

java集合的工具类Collections中提供了两种排序的方法,分别是: Collections.sort(List list)Collections.sort(List list,Comparator c) 第一种称为自然排序,参与排序的对象需实现comparable接口,重写其compareTo()方法,方法体中实现对象的比较大小规则,示例如下: 实体类:(基本…

ubuntu编写python脚本_python在ubuntu中的几种方法(小结)

通过ubuntu官方的apt工具包安装通过PPA(Personal Package Archive) 的apt工具包安装通过编译python源代码安装通过ubuntu官方的apt工具包安装安装完成后&#xff0c; 可以用下面的命令进行确认从PPA(Personal Package Archives) 安装apt工具包类似使用apt工具包安装python的工…

Java中String类中compareTo( )方法

compareTo方法是比较简单的&#xff0c;我们可以直接看其源码: 源码如下&#xff1a; public int compareTo(String anotherString) {int len1 value.length;int len2 anotherString.value.length;int lim Math.min(len1, len2);char v1[] value;char v2[] anotherString…

python elif可以单独使用_Python的elif语句怎么用

else和elif语句也可以叫做子句&#xff0c;因为它们不能独立使用&#xff0c;两者都是出现在if、for、while语句内部的。else子句可以增加一种选择&#xff1b;而elif子句则是需要检查更多条件时会被使用&#xff0c;与if和else一同使用&#xff0c;elif是else if 的简写。if和…

SpringMVC接收哪些类型参数参数

支持的数据类型&#xff1a; 基本类型参数&#xff1a; 包括基本类型和 String 类型 POJO 类型参数&#xff1a; 包括实体类&#xff0c;以及关联的实体类 数组和集合类型参数&#xff1a; 包括 List 结构和 Map 结构的集合&#xff08;包括数组&#xff09; SpringMVC …

禁用当前的账户win7_系统小技巧:服务客人 开启Windows 10来宾账户

出于安全考虑&#xff0c;Windows 10默认以管理员账户登录&#xff0c;没有开启来宾账户。但对于那些只需在电脑上浏览网页或收看电子邮件的用户&#xff0c;给他们开启来宾账户非常必要。来宾权限或账户的开启&#xff0c;可以通过下面的两种方法。1. 通过系统设置 开启来宾权…

Java之接口的静态方法的定义和使用

格式如下&#xff1a;&#xff08;就是将abstract或者default换成ststic即可&#xff0c;带上方法体&#xff09; public static 返回值类型 方法名称&#xff08;参数列表&#xff09;{方法体----}代码如下&#xff1a; //定义一个接口 public interface MyInterfaceStatic …

三阶魔方还原步骤图_三阶魔方公式图解、教程

三阶魔方公式、魔方图解、魔方教程&#xff0c;从零基础到精通&#xff01;魔方还原法 Rubics Cube Solution ————先看理论“魔方的还原方法很多精彩内容&#xff0c;尽在百度攻略&#xff1a;https://gl.baidu.com在这里向大家介绍一种比较简单的魔方六面还原方法。这种方…

通俗易懂告诉你CPU/GPU是什么?

通俗易懂告诉你CPU/GPU是什么&#xff1f; CPU CPU( Central Processing Unit, 中央处理器)就是机器的“大脑”&#xff0c;也是布局谋略、发号施令、控制行动的“总司令官”。 CPU的结构主要包括运算器&#xff08;ALU, Arithmetic and Logic Unit&#xff09;、控制单元&a…

mysql正在加载_mysql 数据库基本操作

CREATE TABLE classes(id INT PRIMARY KEY AUTO_INCREMENT COMMENT班级表id,name VARCHAR(20) COMMENT班级名称);运行DESCRIBE classes;--------------------------------------------------------| Field | Type | Null | Key | Default | Extra |---------------------------…

CPU和GPU的区别是什么?

CPU是一种微处理器&#xff0c;用于执行程序根据操作(如算术、逻辑、控制和输入-输出)给出的指令。相反&#xff0c;GPU最初设计用于在电脑游戏中渲染图像。CPU强调低延迟&#xff0c;而GPU则强调高吞吐量。 CPU Vs GPU 内容 比较表格定义区别关键不同结论 1. 比较表格 从上…

乌班图配置mysql Java_Ubuntu准备+MySQL+Java

Linux服务器准备1 . 安装Ubuntu系统2 . 修改root用户密码sudo passwd root #修改root账户密码logout # 退出当前账号并重新登录#如果需要关机使用 halt 命令3 . 更新所有Linux的开发包apt-get update4 . 配置相关的编译包apt-get install gcc libpcre3 libpcrecpp* libpcre3-de…

java截取指定字符串中的某段字符

利用字符串的substring函数来进行截取。 其中&#xff0c;substring函数有两个参数&#xff1a; 1、第一个参数是开始截取的字符位置。&#xff08;从0开始&#xff09; 2、第二个参数是结束字符的位置1。&#xff08;从0开始&#xff09; indexof函数的作用是查找该字符串中…

JSON中的JSON.parseArray()、JSON.parseObject()、JSON.tojsonString()

1、JSON.parseObject和JSON.toJSONString JSON.parseObject&#xff0c;是将Json字符串转化为相应的对象&#xff1b;JSON.toJSONString则是将对象转化为Json字符串。在前后台的传输过程中&#xff0c;JSON字符串是相当常用的&#xff0c;这里就不多介绍其功能了&#xff0c;直…

mysql是gplv3,Affero-GPL和GPLv3之间的区别

解决方案Assume the following:You are developing a server side application in GPL. Now this application serves HTML and not an executable which is directly executed on your machine. That means that another guy could take the GPL code, adapt it and does not …