一、常用注解详解

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),就可以解除关系,中间表的对应的记录也没有了。
    ​
    ####  **四、一对多和多对一**
    ​
    #####       **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属性决定,标注在那,那个就不维护关系。级联操作是作用于当前类的操作发生时,对关系类进行级联操作。