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

扫描二维码关注公众号,回复: 2138991 查看本文章

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

猜你喜欢

转载自blog.csdn.net/shuixiou1/article/details/80086013