欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring-boot的spring-jpa基本操作以及以及一对多,多对一,多对多

程序员文章站 2022-04-12 20:32:07
...

    一、常用注解详解

   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使用没多大区别啊!