1、全局的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>xyz.haijin</groupId>
<artifactId>springDataTest</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>jpa_hibernate_01</module>
<module>springdata_jpa_02</module>
<module>springdata_jpa_03</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<querydsl.version>4.4.0</querydsl.version>
<apt.version>1.1.3</apt.version>
<mysql.version>8.0.29</mysql.version>
</properties>
<!--统一管理SpringData子项目的版本-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-bom</artifactId>
<version>2021.1.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2、项目的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springDataTest</artifactId>
<groupId>xyz.haijin</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springdata_jpa_03</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- junit4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.32.Final</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</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>
<scope>test</scope>
</dependency>
<!--spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.10</version>
<scope>test</scope>
</dependency>
<!-- querydsl -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt.version}</version>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</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>
</project>
3、SpringDataJPAConfig:
package xyz.haijin.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
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;
/**
* jpa配置
* <p>
* 2024/11/2.
*
* @author haijin
* @version v1
*/
//@Configuration // 标记当前类为配置类 =xml配文件
@EnableJpaRepositories(basePackages="xyz.haijin.repositories") // 启动jpa <jpa:repositories
@EnableTransactionManagement // 开启事务
public class SpringDataJPAConfig {
/*
* <!--数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" name="dataSource">
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springdata_jpa?characterEncoding=UTF-8"/>
</bean>
* */
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("haijinmysql");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.3.10:3306/springdata_jpa?characterEncoding=UTF-8");
return dataSource;
}
/*
* <!--EntityManagerFactory-->
<bean name="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jpaVendorAdapter">
<!--Hibernate实现-->
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--生成数据库表-->
<property name="generateDdl" value="true"></property>
<property name="showSql" value="true"></property>
</bean>
</property>
<!--设置实体类的包-->
<property name="packagesToScan" value="com.tuling.pojo"></property>
<property name="dataSource" ref="dataSource" ></property>
</bean>
* */
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("xyz.haijin.po");
factory.setDataSource(dataSource());
return factory;
}
/*
* <bean class="org.springframework.orm.jpa.JpaTransactionManager" name="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
* */
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
4、Account:
package xyz.haijin.po;
import lombok.Data;
import javax.persistence.*;
/***
*
* 一对一
* 一个客户对一个账户
*/
@Entity
@Table(name="tb_account")
@Data
/*@Getter // 生成所有属性的get方法
@Setter // 生成所有属性的set方法
@RequiredArgsConstructor // 生成final属性的构造函数, 如果没有final就是无参构造函数
@EqualsAndHashCode*/
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@OneToOne
@JoinColumn(name="customer_id")
private Customer customer;
}
5、Customer:
package xyz.haijin.po;
import lombok.Data;
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.List;
@Entity // 作为hibernate 实体类
@Table(name = "tb_customer") // 映射的表明
@Data
@EntityListeners(AuditingEntityListener.class)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long custId; //客户的主键
@Column(name = "cust_name")
private String custName;//客户名称
@Column(name="cust_address")
private String custAddress;//客户地址
// 单向关联 一对一
/*
* cascade 设置关联操作
* ALL, 所有持久化操作
PERSIST 只有插入才会执行关联操作
MERGE, 只有修改才会执行关联操作
REMOVE, 只有删除才会执行关联操作
fetch 设置是否懒加载
EAGER 立即加载(默认)
LAZY 懒加载( 直到用到对象才会进行查询,因为不是所有的关联对象 都需要用到)
orphanRemoval 关联移除(通常在修改的时候会用到)
一旦把关联的数据设置null ,或者修改为其他的关联数据, 如果想删除关联数据, 就可以设置true
optional 限制关联的对象不能为null
true 可以为null(默认 ) false 不能为null
mappedBy 将外键约束执行另一方维护(通常在双向关联关系中,会放弃一方的外键约束)
值= 另一方关联属性名
**/
@OneToOne(mappedBy = "customer",
cascade = CascadeType.ALL,fetch = FetchType.LAZY,orphanRemoval=true/*,optional=false*/)
// 设置外键的字段名
@JoinColumn(name="account_id")
private Account account;
// 一对多
// fetch 默认是懒加载 懒加载的优点( 提高查询性能)
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
@JoinColumn(name="customer_id")
private List<Message> messages;
// @Override
// public String toString() {
// return "Customer{" +
// "custId=" + custId +
// ", custName='" + custName + '\'' +
// ", custAddress='" + custAddress + '\'' +
// ", account=" + account +
// ", messages=" + messages.toString() + // 会用到懒加载的数据, 用到的时候就会执行懒加载查询
// '}';
// }
// 单向多对多
@ManyToMany(cascade = CascadeType.ALL)
/*中间表需要通过@JoinTable来维护外键:(不设置也会自动生成)
* name 指定中间表的名称
* joinColumns 设置本表的外键名称
* inverseJoinColumns 设置关联表的外键名称
* */
@JoinTable(
name="tb_customer_role",
joinColumns = {@JoinColumn(name="c_id")},
inverseJoinColumns = {@JoinColumn(name="r_id")}
)
private List<Role> roles;
private @Version Long version;
@CreatedBy
String createdBy;
@LastModifiedBy
String modifiedBy;
/**
* 实体创建时间
*/
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
protected Date dateCreated = new Date();
/**
* 实体修改时间
*/
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
protected Date dateModified = new Date();
}
6、Message:
package xyz.haijin.po;
import lombok.Data;
import javax.persistence.*;
/***
* 一(客户)对多(信息)
*/
@Entity
@Table(name="tb_message")
@Data
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String info;
public Message(String info) {
this.info = info;
}
public Message(String info, Customer customer) {
this.info = info;
this.customer = customer;
}
// 一定要有、否则查询就会有问题
public Message() {
}
// 多对一
@ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.REMOVE})
@JoinColumn(name="customer_id")
private Customer customer;
@Override
public String toString() {
return "Message{" +
"id=" + id +
", info='" + info + '\'' +
", customerId=" + customer.getCustId() +
", customerName=" + customer.getCustName() +
'}';
}
}
7、Role:
package xyz.haijin.po;
import lombok.Data;
import javax.persistence.*;
import java.util.List;
/***
*
* 多(用户)对多(角色)
*/
@Entity
@Table(name="tb_role")
@Data
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="role_name")
private String rName;
public Role(String rName) {
this.rName = rName;
}
public Role(Long id, String rName) {
this.id = id;
this.rName = rName;
}
public Role() {
}
@ManyToMany(cascade = CascadeType.ALL)
private List<Role> roles;
}
8、CustomerRepository:
package xyz.haijin.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import xyz.haijin.po.Customer;
public interface CustomerRepository extends PagingAndSortingRepository<Customer,Long>{
}
9、MessageRepository:
package xyz.haijin.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Message;
import java.util.List;
public interface MessageRepository extends PagingAndSortingRepository<Message,Long>{
// 根据客户id查询所有信息
// 通过规定方法名来实现关联查询: 需要通过关联属性来进行匹配
// 但是只能通过id来进行匹配
List<Message> findByCustomer(Customer customer);
}
10、RoleRepository:
package xyz.haijin.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import xyz.haijin.po.Role;
public interface RoleRepository extends PagingAndSortingRepository<Role,Long>{
}
11、ManyToManyTest:
package xyz.haijin;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Commit;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Role;
import xyz.haijin.repositories.CustomerRepository;
import xyz.haijin.repositories.RoleRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToManyTest {
@Autowired
CustomerRepository repository;
@Autowired
RoleRepository roleRepository;
// 保存
/*
1.如果保存的关联数据 希望使用已有的 ,就需要从数据库中查出来(持久状态)。否则 提示 游离状态不能持久化
2.如果一个业务方法有多个持久化操作, 记得加上@Transactional ,否则不能共用一个session
3. 在单元测试中用到了@Transactional , 如果有增删改的操作一定要加@Commit
4. 单元测试会认为你的事务方法@Transactional, 只是测试而已, 它不会为你提交事务, 需要单独加上 @Commit
*/
@Test
@Transactional
@Commit
public void testC() {
List<Role> roles=new ArrayList<>();
roles.add(roleRepository.findById(9L).get());
roles.add(roleRepository.findById(10L).get());
Customer customer = new Customer();
customer.setCustName("诸葛");
customer.setRoles(roles);
repository.save(customer);
}
@Test
@Transactional(readOnly = true)
public void testR() {
System.out.println(repository.findById(14L));
//repository.save(customer);
}
/*
* 注意加上
* @Transactional
@Commit
多对多其实不适合删除, 因为经常出现数据出现可能除了和当前这端关联还会关联另一端,此时删除就会: ConstraintViolationException。
* 要删除, 要保证没有额外其他另一端数据关联
* */
@Test
@Transactional
@Commit
public void testD() {
Optional<Customer> customer = repository.findById(14L);
repository.delete(customer.get());
}
}
12、ManyToOneTest:
package xyz.haijin;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Message;
import xyz.haijin.repositories.MessageRepository;
import java.util.ArrayList;
import java.util.List;
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToOneTest {
@Autowired
MessageRepository repository;
// 多对一 插入
// 得出: 当插入"多"的数据的时候,使用多对一的关联关系是更加合理
@Test
public void testC(){
// 一
Customer customer = new Customer();
customer.setCustName("司马懿");
// 多
List<Message> list=new ArrayList<>();
list.add(new Message("你好",customer));
list.add(new Message("在吗?",customer));
repository.saveAll(list);
}
// 多对一:根据客户id查询对应的所有信息
// 通过“一”进行条件查询, 在一对多中实现是更合理的
@Test
public void testR(){
Customer customer = new Customer();
customer.setCustId(1L);
customer.setCustName("xxx");
List<Message> messages = repository.findByCustomer(customer);
// 隐式调用toString()
System.out.println(messages);
}
@Test
public void testD(){
Customer customer = new Customer();
customer.setCustId(1L);
List<Message> messages = repository.findByCustomer(customer);
repository.deleteAll(messages);
}
}
13、OneToManyTest:
package xyz.haijin;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Commit;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Message;
import xyz.haijin.repositories.CustomerRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToManyTest {
@Autowired
CustomerRepository repository;
// 插入
@Test
public void testC(){
List<Message> messageList=new ArrayList<>();
messageList.add(new Message("您好"));
messageList.add(new Message("在吗?"));
Customer customer = new Customer();
customer.setCustName("徐庶帅哥");
customer.setMessages(messageList);
repository.save(customer);
}
// 插入
@Test
@Transactional(readOnly = true)
public void testR(){
// 懒加载过程:
// 1.findById 只会查询Customer 和其他关联的立即加载
Optional<Customer> customer = repository.findById(1L);
System.out.println("=====================");
// 由于输出, 会自动调用customer.toString()
System.out.println(customer);
}
// 插入
@Test
public void testD(){
repository.deleteById(2L);
}
@Test
@Transactional
@Commit
public void testU(){
Optional<Customer> customer = repository.findById(19L);
customer.get().setCustName("xxx");
}
}
14、OneToOneTest:
package xyz.haijin;
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 org.springframework.transaction.annotation.Transactional;
import xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Account;
import xyz.haijin.po.Customer;
import xyz.haijin.repositories.CustomerRepository;
import java.util.Optional;
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToOneTest {
@Autowired
CustomerRepository repository;
// 插入
@Test
public void testC(){
// 初始化数据
Account account = new Account();
account.setUsername("xushu");
Customer customer = new Customer();
customer.setCustName("haijin");
customer.setAccount(account);
account.setCustomer(customer);
repository.save(customer);
}
// 插入
@Test
// 为什么懒加载要配置事务 :
// 当通过repository调用完查询方法,session就会立即关闭, 一旦session你就不能查询,
// 加了事务后, 就能让session直到事务方法执行完毕后才会关闭
@Transactional(readOnly = true)
public void testR(){
Optional<Customer> customer = repository.findById(3L); // 只查询出客户, session关闭
System.out.println("=================");
System.out.println(customer.get()); // toString
}
@Test
public void testD(){
repository.deleteById(1L);
}
@Test
public void testU(){
Customer customer = new Customer();
customer.setCustId(16L);
customer.setCustName("haijin");
customer.setAccount(null);
repository.save(customer);
}
}