一、常用注解详解
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属性决定,标注在那,那个就不维护关系。级联操作是作用于当前类的操作发生时,对关系类进行级联操作。