spring-boot的spring-jpa基本操作以及以及一对多,多对一,多对多
一、常用注解详解
1、@Entity+@Table 标注在实体类上,表示是一个实体,并且如果表名和实体类名一样,可以省略table,否则加上@Table(name="表名")
2、@NoRepositoryBean 标注在父类中repository,表示spring不会去实例化它。
3、@Column:标注在属性上,如果字段名与列名相同,则可以省略。
4、 @Id:标注在主键上,表示该属性为主键。一般还结合了@GeneratedValue(),主键的策略,默认是自增,等同于 @GeneratedValue(strategy= GenerationType.AUTO)
5、@Transient:表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic。@Basic(fetch=FetchType.LAZY):标记可以指定实体属性的加载方式
6、@JoinColumn(name=”role_id”): 标注在连接的属性上(一般多对1的1),指定了本类用1的外键名叫什么。
@JoinTable(name="996_permission_role") :标注在连接的属性上(一般多对多),指定了多对多的中间表叫什么。
备注:Join的标注,和下面几个标注的mappedBy属性互斥!
7、@ManyToMany、@OneToMany、@ManyToOne:标注在连接的属性上,多对多,1对多,多对1
属性1: mappedBy="permissions" 表示,当前类不维护状态,属性值其实是本类在被标注的链接属性上的链接属性,此案例的本类时Permission,连接属性是roles,连接属性的类的连接属性是permissions
属性2: fetch = FetchType.LAZY 表示是不是懒加载,默认是,可以设置成FetchType.EAGER
属性3:cascade=CascadeType.ALL 表示当前类操作时,被标注的连接属性如何级联,比如班级和学生是1对多关系,cascade标注在班级类中,那么执行班级的save操作的时候(班级.学生s.add(学生)),能级联保存学生,否则报错,需要先save学生,变成持久化对象,在班级.学生s.add(学生)
注意:只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;
二、单表的几种操作方式
2.1 实体类
@Entity
@Table(name="996_item")
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.AUTO) /** 默认就是 **/
private Integer id;
private String name;
private Float price;
@Column(name = "modify_Time")
private Date modifyTime;
private String detail;
2.2 dao
public interface ItemDao extends JpaRepository<Item,Integer>{
/**
* 方法名的查询方式
*/
List<Item> getItemByNameOrPrice(String name,Float price);
/**
* hql的查询方式
*/
@Query("from Item where name=?1 or price=?2")
List<Item> getItemByNameOrPrice2(String name,Float price);
/**
* 本地查询的方式
*/
@Query(value="select * from 996_item where name = :name or price = :price",nativeQuery=true)
List<Item> getItemByNameOrPrice3(@Param("name")String name,@Param("price")Float price);
/**
* api 操作的方式 ---->写在测试类中了
***/
//.....其他方式,比如命名查询等。
}
2.3 测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=Application.class)
public class TestItem {
@Autowired
private ItemDao itemdao;
@PersistenceContext
protected EntityManager em;
/**
* 方式1:使用getItemByName这种标准的接口方法,框架自动生成实现!
* 方式2:使用自定义的jpql语句
* 方式3:使用本地语句
*/
@Test
public void test1(){
Item item = new Item();
item.setName("张飞");
item.setPrice((float) 1.2);
itemdao.save(item);
List<Item> item1 = itemdao.getItemByNameOrPrice("张飞",(float) 1.2); //方式1
List<Item> item2 = itemdao.getItemByNameOrPrice2("张飞",(float) 1.2); //方式2
List<Item> item3 = itemdao.getItemByNameOrPrice3("张飞",(float) 1.2); //方式3
System.out.println(item1.get(0).toString());
System.out.println(item2.get(0).toString());
System.out.println(item3.get(0).toString());
}
/**
* Api的操作方式。
*/
@Test
public void test2(){
Query query = em.createNativeQuery("select * from 996_item where name = ? or price = ? ", Item.class);
query.setParameter(1, "张思");
query.setParameter(2, (float) 1.2);
List list = query.getResultList();
System.out.println(list.get(0));
}
}
三、多对多 : 演示是权限表和角色表的多对多关系
3.1 实体类
/**
* 权限表
*/
@Entity
@Table(name="996_permission")
public class Permission{
@Id
@GeneratedValue()
private Integer id;
private String name;
private String type;
private String url;
@Column(name="perm_code")
private String permCode;
/**
* 注意不能2边用mappedBy:这个属性就是维护关系的意思!谁主类有此属性谁不维护关系。
* 比如2个多对多的关系是由role中的permissions维护的,那么,只有操作role方,指定permissions,才可建立外键的关系。
* 注意:只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性; 并且mappedBy一直和joinXX互斥。
*/
@ManyToMany(mappedBy="permissions",fetch = FetchType.LAZY)
private Set<Role> roles;
/**
* 角色表
*/
@Entity
@Table(name="996_role")
public class Role{
@Id
@GeneratedValue()
private Integer id;
private String name;
/**
* cascade表示级联操作,all是全部,一般用MERGE 更新,persist表示持久化即新增
* 此类是维护关系的类,删除它,可以删除对应的外键,但是如果需要删除对应的权限就需要CascadeType.all
* cascade:作用在本放,对于删除或其他操作本方时,对标注连接方的影响!和数据库一样!!
*/
@ManyToMany(cascade = CascadeType.MERGE,fetch = FetchType.LAZY)
@JoinTable(name="996_permission_role")
private Set<Permission> permissions = new HashSet<Permission>();
@OneToMany(mappedBy="role",fetch=FetchType.LAZY,cascade=CascadeType.ALL)
private Set<User> users = new HashSet<User>();
注解中属性的汉语解释:权限不维护关系,关系表是996_permission_role,全部懒加载,角色的级联是更新 (多对多关系不适合用all,不然删除一个角色,那么所有此角色对应的权限都被删了,级联删除一般用于部分一对多时业务需求上是可删的,比如品牌类型就不适合删除一个类型就级联删除所有的品牌,一般是把此品牌的类型设置为null(解除关系),然后执行删除,就不会报错了!)
3.2 Dao就默认继承了extends JpaRepository<Role,Integer>
3.3 测试
/**
* 测试角色
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=Application.class)
@Transactional(transactionManager="transactionManager")
@Rollback(false) /**测试环境必须false!否则会出现不可预期的各种诡异现象!!!加事务操作有时候能执行(无异常),有时候会回滚(无异常)**/
public class TestRole {
@Autowired
private RoleDao roleDao;
@Autowired
private PermissionDao dao;
@Autowired
private UserDao userDao;
@Test
public void test1(){
Role role1 = new Role();
role1.setName("管理员");
Set<Permission> ps = new HashSet<Permission>();
for (int i = 0; i < 3; i++) {
Permission pm = new Permission();
pm.setName("操作"+i);
dao.save(pm); /**由于我的Role类没有设置级联持久化,所以这里需要先持久化pm,否则报错!*/
ps.add(pm);
}
role1.setPermissions(ps);
roleDao.save(role1);
}
@Test
public void test2(){
Role one = this.roleDao.findOne(1);
this.roleDao.delete(one); /**当Role类不设置级联删除的时候,就不会级联删除permissions*/
}
@Test
public void test3(){
Role one = this.roleDao.findOne(1);
one.setPermissions(null); /**设置成null,可以删除中间表*/
}
说明:test1我们可以看到,由于role方是维护关系的,所以建立Roles.set(Permissions)就能把关系表建立,但是注意一点,由于我没有设置级联=all,而Permissions是个临时对象,而临时对象保存时会持久化,如果不是我级联保存的话,那么会报错,解决办法如测试范例,先通过save(pm),再操作。
test2我们可以观察到,当执行完后,中间表的删除是由维护关系的role删除了(自己都删除了,关系肯定也需要维护的),但是,permission表还存在数据。
test3我们可以观察到,我把role.setPermission(null),就可以解除关系,中间表的对应的记录也没有了。
四、一对多和多对1
4.1 实体类
/**
* 用户表,和权限多对1
*/
@Entity
@Table(name="996_user")
public class User{
@Id
@GeneratedValue()
private Integer id;
private String username;
private String password;
private String salt;
private Integer locked;
@Column(name="create_time")
private Date createTime;
@Column(name="update_time")
private Date updateTime;
/**1对多,多的一方必须维护关系,即不能指定mapped=""**/
@ManyToOne(fetch = FetchType.LAZY,cascade=CascadeType.MERGE)
@JoinColumn(name="role_id")
private Role role;
配置汉语说明:由于多对1不能用mapped那么,它必然必须维护关系,即mapped属性是在1的一方,维护关系是多的一方由User维护的,User的级联是更新,Role的级联是All,User的外键是role_id指向Role。
4.2 dao public interface UserDao extends JpaRepository<User,Integer>{
4.3 测试:
@Test
public void test4(){
Role role = new Role();
role.setName("角色1");
User user1 = new User();
user1.setUsername("张1");
User user2 = new User();
user2.setUsername("张2");
role.getUsers().add(user1);
role.getUsers().add(user2);
roleDao.save(role);/**这样能够级联插入user,级联标注在那个类,这个类的对应操作就会影响连接的属性的表!**/
}
@Test
public void test5(){
Role role = new Role();
role.setName("角色1");
User user1 = new User();
user1.setUsername("张1");
user1.setRole(role);
User user2 = new User();
user2.setUsername("张2");
user2.setRole(role);
//this.userDao.save(user1); //会报错!
//this.userDao.save(user2);
//因为我的User方配置cascade = MERGE,所以我保存user,不能级联保存一个user,而user是一个未持久的对象,因此必须级联保存!
//解决方案!
this.roleDao.save(role);
this.userDao.save(user1);//因为我先保存了role,所以添加user的时候,就不是把一个游离对象持久化,即是合法的操作了!
}
五、总结:
维护关系是由mapped属性决定,标注在那,那个就不维护关系。级联操作是作用于当前类的操作发生时,对关系类进行级联操作。
和hibernate使用没多大区别啊!
上一篇: meta标签中的viewport
下一篇: hibernate 自定义注解参数校验