Spring Data
Spring Data JPA
查询方式
查询方式有五种:
- 调用父接口中的方法
- 方法命名规则查询(按照一定的规范定义接口中的方法, 自动生成SQL)
- JPQL查询
- 本地SQL查询
- Specification动态查询
多表关系
OneToOne 关系
(略)
OneToMany 关系
One:主表
Many:从表
OneToMany 关系通过从表的外键来刻画
ManyToMany 关系
不区分主表和从表,通过中间表来代替外键,对 ManyToMany 关系进行刻画
OneToMany 和 ManyToMany 的区别:
- OneToMany 关系中,站在主表到从表的角度看,是一对多关系;而站在从表到主表的角度看,是一对一关系。
- ManyToMany 关系中,无论站在两张表的那一方去看,都是一对多关系。
所谓的维护关系和放弃关系的维护权,本质上就是在调用save()方法时,需不需要额外去保存关系(可能是中间表,也可能是外键)。在保存维护关系的类之前,需要将不维护关系的实体类先保存。如果先保存维护关系的实体类A,那么同时触发保存关系表,而关系表设计到实体类A和实体类B,此时实体类B还没有保存,因此会报错。

案例演示
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "article")
@Getter
@Setter
public class Article {
/**
* GenerationType.IDENTITY: 数据库的主键需要设置为AUTO_INCREMENT才会生效, 否则报错
* <p>
* 虽然id可以唯一的标识一个对象, 但是如果重写hashCode和equals方法并不可以仅仅通过id, 因为在插入的时候, id还并没有生成
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "title")
private String title;
@Column(name = "author")
private String author;
@Column(name = "create_time")
private Date createTime;
@Column(name = "update_time")
private Date updateTime;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 上面设置Article本身的属性字段, 下面设置与其它类的关系
// 使用@JoinTable表示负责维护关系, 需要后保存
@ManyToMany
@JoinTable(
// 中间表在数据库中的名称
name = "article_types",
// 中间表中对应到本表主键的列名, 因为主键可能是多列共同组成的一个复合列, 因此使用数组
joinColumns = {
@JoinColumn(name = "aid", referencedColumnName = "id")
},
// 中间表中对应到另一张表主键的列名
inverseJoinColumns = {
@JoinColumn(name = "tid", referencedColumnName = "id")
}
)
private Set<Type> types = new HashSet<>();
public void addType(Type type) {
this.types.add(type);
}
}
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Getter
@Setter
@Entity
@Table(name = "type")
public class Type {
/**
* 类型id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 类型名称
*/
private String name;
/**
* mappedBy用来表示主动放弃关系的维护权
*/
@ManyToMany(mappedBy = "types")
private Set<Article> articles = new HashSet<>();
public void addArticle(Article article) {
this.articles.add(article);
}
}
@SpringBootTest
public class ApplicationMainTest {
@Autowired
private ArticleRepository articleRepository;
@Autowired
private TypeRepository typeRepository;
@Test
public void manyToManyRelationshipTest() {
// 创建Article
Article article01 = new Article();
article01.setAuthor("Root");
article01.setTitle("JPA");
article01.setCreateTime(new Date());
Article article02 = new Article();
article02.setAuthor("Admin");
article02.setTitle("Spring Data");
article02.setCreateTime(new Date());
// 创建Type
Type type01 = new Type();
type01.setName("TypeA");
Type type02 = new Type();
type02.setName("TypeB");
// 设置关系
article01.addType(type01);
article01.addType(type02);
article02.addType(type01);
article02.addType(type02);
// TODO: 1. 为什么会在这里报错java.lang.StackOverflowError呢?
// 上面的也没有报错. hashCode的问题.
// 并且奇怪的是, 在将@Data注解换成getter和setter方法后就正常了
// [解决]: 由于两个循环依赖的对象会互相调用toString方法, 从而导致栈溢出, @Data注解中默认包含@ToString注解. 可以显式地使用@ToString注解
type01.addArticle(article01);
type01.addArticle(article02);
type02.addArticle(article01);
type02.addArticle(article02);
// TODO: 为什么在保存成功后(insert)会执行select和update?
// [解决]: 莫名其妙地解决
// 和OneToMany关系一样, 需要先保存不维护关系的实体Type, 否则会报错.
// 不维护关系的实体可以认为仅仅保存自己, 不会产生循环依赖.
// 而维护关系的实体, 在保存自己的时候, 还要去保存关系, 因此如果此时type没有保存, 就不会有tid, 因此报错
typeRepository.save(type01);
typeRepository.save(type02);
articleRepository.save(article01);
articleRepository.save(article02);
}
}

级联操作(需要改进)
TODO:对级联操作的理解有误,下面的涉及到cascade的不一定正确,有待改进。
操作一个对象时,同时操作与他相关联的对象。
以User和Role为例,User和Role是多对多关系,数据库中有三张表
user、role、user_role
情况1:User和Role都不使用
@JoinTable(没有维护关系)userDao.save(user); roleDao.save(role); // 此时user表和role表中都有数据, 但是user_role表中没有数据 // 维护关系发生在save()时, 但是User和Role都没有去维护关系, 即都没有向user_role表中添加数据情况2:User和Role都使用
@JoinTable(都维护关系)(TODO:期待报错主键重复,目前测试失败)
userDao.save(user); roleDao.save(role); // 此时roleDao.save()时出现主键重复的问题, // 假设userId=1, roleId=2, 在userDao.save()时向user_role表中添加数据, 但是在roleDao.save()时再次向user_role表中添加相同的主键情况2改进:User和Role任意一方放弃关系的维护,即调用save方法不会向user_role表中添加数据
放弃关系的维护,通过
@ManyToMany中的mappedBy属性(TODO:期待在任意一方添加上mappedBy属性后执行成功,目前测试失败)
userDao.save(user); roleDao.save(role);情况3:在负责维护关系的实体类中使用级联操作(cascade)
不妨假设维护关系的实体类为User,那么期待仅仅通过userDao.save()就可以同时保存role
userDao.save(user);
Spring Data Redis
Spring Data Redis中提供的序列化器,可以通过 RedisSerializer 接口来查看有哪些序列化器
| 序列化器 | 作用 |
|---|---|
| StringRedisSerializer | 简单的字符串序列化 |
| GenericToStringSerializer | 可以将任何对象泛化为字符串并序列化 |
| Jackson2jsonRedisSerializer | 序列化对象为json字符串 |
| GenericJackson2jsonRedisSerializer | 序列化对象为json字符串,更容易反序列化 |
| OxmSerializer | 序列化对象为xml字符串 |
| JdkSerializationRedisSerializer(默认) | 序列化对象为二进制数据 |