2020-11-21 更新:解決由于.yml文件引起的數(shù)據(jù)庫鏈接問題
文章目錄 一、前言 二、項目環(huán)境 三、項目文件結構 四、項目代碼 數(shù)據(jù)庫連接配置 1.Entity層 2.dao層 3.service層 4.controller層 五、運行效果 六、參考資料
一、前言
為了避免浪費時間進行不必要的閱讀,這里先對項目進行簡單的介紹。在實際應用場景中,每個用戶都有對應的角色,而每個角色又有對應的一些角色。因為一個用戶可以有多個角色,一個角色也可以被多個用戶所擁有,角色和權限的關系也同理,這里主要利用多對多的映射關系 將他們聯(lián)系起來,對他們進行管理。
主要實現(xiàn)的功能:
添加用戶、角色和權限 刪除用戶、角色和權限 給用戶添加角色、給角色添加權限 根據(jù)用戶名稱查詢用戶擁有的權限
二、項目環(huán)境
Java版本:jdk1.8.0_181 IDE:IntelliJ IDEA 2019.1.3 數(shù)據(jù)庫:postgresql 9.5 測試工具:postman
ps:數(shù)據(jù)庫類型不同不要緊,在創(chuàng)建的時候勾選不一樣的數(shù)據(jù)庫驅動的就行。
三、項目文件結構
項目創(chuàng)建的時候,需要勾選Web中的Spring Web Starter 和SQL中Spring Data JPA、PostgreSQL Driver (如果使用的是mysql數(shù)據(jù)庫,則勾選MySQL Driver),IDEA會自動幫我們在Maven的配置文件中添加相關的依賴。
以下是本項目的目錄結構:
四、項目代碼
數(shù)據(jù)庫連接配置
spring : datasource : driver-class-name : org.postgresql.Driver
username : postgres
password : 123456 url : jdbc
: postgresql
: //localhost
: 5432/postgres
jpa : hibernate : ddl-auto : update
show-sql : true properties : hibernate : temp : use_jdbc_metadata_defaults : false
如果遇到數(shù)據(jù)庫連接不成功的問題,可以嘗試將 properties: 以及之后的部分刪除。
1.Entity層
Entity層為數(shù)據(jù)庫實體層,一般一個實體類對應數(shù)據(jù)庫中的一張數(shù)據(jù)表,類中的屬性與數(shù)據(jù)表中的字段一 一對應。默認情況下,類名即為數(shù)據(jù)表的表名,屬性名則是對應字段名,字段類型也與變量的類型相對應。
本層注解簡單解釋:
@Entity 該注解用于表明這個類是一個實體類,會給他生成一張對應的數(shù)據(jù)表。 @Table(name = “table_name”) 該注解主要用于修改表名,name的值就是修改的數(shù)據(jù)表的名稱。 @Id 該注解用于聲明主鍵,標在哪個屬性上面對應的哪個字段就是主鍵 @GeneratedValue(strategy = GenerationType.IDENTITY) 該注解的strategy屬性主要用于設置主鍵的增長方式,IDENTITY表示主鍵由數(shù)據(jù)庫自己生成,從1開始單調遞增。 @Column(name = “column_name”) 該注解的name屬性用于更改數(shù)據(jù)表的列名,如果不想用默認的就用這個屬性改吧 @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 這個注解得上是本項目得核心了,它聲明了實體之間的多對多關系,使兩張數(shù)據(jù)表關聯(lián)關聯(lián)起來,一般是通過生成一張映射表來實現(xiàn)這種映射關系 。關于上面的cascade屬性和fetch屬性,有興趣的讀者可以查資料了解。 @JoinTable 這個注解是配套@ManyToMany使用的,一般在多對多關系的維護端標注,用于生成上面提到的映射表。一般該注解常用三個屬性:name屬性表示生成的數(shù)據(jù)表的名稱,joinColumns屬性表示關系維護端的主鍵,inverseJoinColumns則表示關系被維護端的主鍵 。關于嵌套在里面的@JoinColumn注解,在這里主要用于配置映射表的外鍵,一般有兩個屬性:name用于配置外鍵在映射表中的名稱,referencedColumnName 用于表明外鍵在原表中的字段名稱。@JsonBackReference 關于這個注解,建議先去掉試試然后再加上,對比一下效果。它主要可以使標注屬性避免被json序列化 ,進而避免多對多關系的查詢中出現(xiàn)死循環(huán)的情況。但是加上了這注解后,就不能進行反向查詢了(也就是說不能利用權限名查詢擁有這個權限的角色了)
注意:以下代碼都省略了要導入的包,getter和setter方法。需要導入相關包可以用快捷鍵Alt+Insert,用快捷鍵Alt+Insert然后選擇Getter and Setter可以快速生成相關方法。
@Entity
@Table ( name
= "user_tabel" )
public class User { @Id @GeneratedValue ( strategy
= GenerationType
. IDENTITY
) @Column ( name
= "user_id" ) private Integer userId
; @Column ( name
= "user_name" ) private String userName
; @ManyToMany ( cascade
= CascadeType
. ALL
, fetch
= FetchType
. LAZY
) @JoinTable ( name
= "user_role" , joinColumns
= { @JoinColumn ( name
= "user_id" ) } , inverseJoinColumns
= { @JoinColumn ( name
= "role_id" ) } ) private List
< Role> roles
;
}
@Entity
@Table ( name
= "role_table" )
public class Role { @Id @GeneratedValue ( strategy
= GenerationType
. IDENTITY
) @Column ( name
= "role_id" ) private Integer roleId
; @Column ( name
= "role_name" ) private String roleName
; @JsonBackReference @ManyToMany ( mappedBy
= "roles" ) private List
< User> users
; @ManyToMany ( cascade
= CascadeType
. ALL
, fetch
= FetchType
. LAZY
) @JoinTable ( name
= "role_auth" , joinColumns
= @JoinColumn ( name
= "role_id" ) , inverseJoinColumns
= @JoinColumn ( name
= "auth_id" ) ) private List
< Authority> authorities
;
}
@Entity
@Table ( name
= "auth_table" )
public class Authority { @Id @GeneratedValue ( strategy
= GenerationType
. IDENTITY
) @Column ( name
= "auth_id" ) private Integer authorityId
; @Column ( name
= "auth_name" ) private String authorityName
; @JsonBackReference @ManyToMany ( mappedBy
= "authorities" ) private List
< Role> roles
;
}
2.dao層
dao層是數(shù)據(jù)持久層,也被稱為mapper層。主要負責訪問數(shù)據(jù)庫,向數(shù)據(jù)庫發(fā)送SQL語句,完成基礎的增刪查改任務。主要通過定義繼承JpaRepository類的接口來實現(xiàn),<>中填寫的是實體類的名稱和該實體主鍵的變量類型。
在接口中聲明的方法不用我們去實現(xiàn),只要滿足命名規(guī)則JpaRepository類會自動幫我們生成相應的sql語句 。 詳情見:官方文檔
public interface UserRepository extends JpaRepository < User, Integer> { public List
< User> findAllByUserName ( String userName
) ; public void deleteByUserName ( String userName
) ;
}
public interface RoleRepository extends JpaRepository < Role, Integer> { public List
< Role> findAllByRoleName ( String roleName
) ; public void deleteByRoleName ( String roleName
) ;
}
public interface AuthorityRepository extends JpaRepository < Authority, Integer> { public List
< Authority> findAllByAuthorityName ( String authorityName
) ; public void deleteByAuthorityName ( String authorityName
) ;
}
3.service層
service層是業(yè)務邏輯層,主要通過調用dao層的接口,接收dao層返回的數(shù)據(jù),完成項目的基本功能設計 。由于本項目的service層是在后面才加的,所以有些應該在本層實現(xiàn)的功能寫在了controller層orz。
踩到的坑
涉及到兩張表以上的更新或者刪除操作,為了保證數(shù)據(jù)庫的一致性,需要添加 @Transactional事務注解,否則程序會拋出異常。(關于事務的詳情,如果不熟悉的話,強烈建議去弄懂。) 如果要執(zhí)行刪除操作,需要先把它的List先清空,也就相當于把映射表中的關系清除。否則會拋出org.hibernate.exception.ConstraintViolationException異常。(我這里用到了多種清除方式:如果刪除維護端數(shù)據(jù),只是把維護端的List清空就行;如果刪除被維護端的數(shù)據(jù),則把用戶(維護端)的List中要移除的角色(被維護端)都remove掉,不知道我是不是想多了)
@Service
public class EntityService { @Autowired private UserRepository userRepository
; @Autowired private RoleRepository roleRepository
; @Autowired private AuthorityRepository authorityRepository
; @Transactional public void deleteUser ( String userName
) { List
< User> users
= userRepository
. findAllByUserName ( userName
) ; for ( User user
: users
) { user
. getRoles ( ) . clear ( ) ; userRepository
. save ( user
) ; } userRepository
. deleteByUserName ( userName
) ; } @Transactional public void deleteRole ( String roleName
) { List
< Role> roles
= roleRepository
. findAllByRoleName ( roleName
) ; List
< User> users
= userRepository
. findAll ( ) ; for ( User user
: users
) { List
< Role> userRole
= user
. getRoles ( ) ; for ( Role role
: roles
) { if ( userRole
. contains ( role
) ) { userRole
. remove ( role
) ; } role
. getAuthorities ( ) . clear ( ) ; roleRepository
. save ( role
) ; } userRepository
. save ( user
) ; } roleRepository
. deleteByRoleName ( roleName
) ; } @Transactional public void deleteAuthority ( String authName
) { List
< Authority> authorities
= authorityRepository
. findAllByAuthorityName ( authName
) ; List
< Role> roles
= roleRepository
. findAll ( ) ; for ( Role role
: roles
) { List
< Authority> roleAuthoritis
= role
. getAuthorities ( ) ; for ( Authority authority
: authorities
) { if ( roleAuthoritis
. contains ( authority
) ) { roleAuthoritis
. remove ( authority
) ; } } roleRepository
. save ( role
) ; } authorityRepository
. deleteByAuthorityName ( authName
) ; } }
4.controller層
controller層是控制層,其功能為請求和響應控制,負責前后端交互,接受前端請求,調用service層,接收service層返回的數(shù)據(jù),最后返回具體的頁面和數(shù)據(jù)到客戶端。
本層注解簡單解釋:
@RestController Spring4之后新加入的注解,相當于@Controller + @ResponseBody。 @Controller 將當前修飾的類注入SpringBoot IOC容器,使得從該類所在的項目跑起來的過程中,這個類就被實例化。當然也有語義化的作用,即代表該類是充當Controller的作用 @ResponseBody 它的作用簡單來說說就是指該類中所有的API接口返回的數(shù)據(jù),甭管你對應的方法返回Map或是其他Object,它會以Json字符串的形式返回給客戶端,根據(jù)嘗試,如果返回的是String類型,則仍然是String。
@RequestMapping("/user") 該注解用來處理請求地址的映射,可用于類或方法上。用于類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。
@Autowired 養(yǎng)成看源代碼的好習慣,在IDEA中按住Ctrl鍵點擊該注解,可以查看該注解的解析。我理解了一下,大概就是調用這個類的構造方法對這個類進行實例化操作。
@RequestParam(value = “userName”) 該注解可以獲取請求報文中的數(shù)據(jù)(數(shù)據(jù)一般以鍵值對方式傳輸),把然后把獲取到的數(shù)據(jù)復制給方法的參數(shù)中。例如上面就是獲取名為"userName"的數(shù)據(jù)值。
再簡單介紹一下,增加用戶、角色和權限的操作。一般我們添加的時候,是先添加權限,再添加角色,最后添加角色 (可以聯(lián)想一下,是不是先有權限才能給角色分配呀)。有些人會對如何關聯(lián)用戶和角色、角色和權限有疑惑(包括一開始的自己),在實體類中存在一個List對象,只要在其中添加對應的對象映射表中就會創(chuàng)建好映射關系。 (可以看看下面添加的代碼,然后自己做實驗觀察現(xiàn)象)
只要這個不是你的第一個spring boot程序,相信你都看得懂。如果感覺功能不夠,讀者還可以自行添加。
@RestController
@RequestMapping ( "/user" )
public class EntityController { @Autowired private UserRepository userRepository
; @Autowired private RoleRepository roleRepository
; @Autowired private AuthorityRepository authorityRepository
; @Autowired private EntityService entityService
; @RequestMapping ( "/finduser" ) public List
< User> findByName ( @RequestParam ( value
= "userName" ) String userName
) { return userRepository
. findAllByUserName ( userName
) ; } @RequestMapping ( "/findalluser" ) public List
< User> findAllUser ( ) { return userRepository
. findAll ( ) ; } @RequestMapping ( "/adduser" ) public List
< User> addUser ( @RequestParam ( value
= "userName" ) String userName
, @RequestParam ( value
= "roleName" ) String roleName
) { User user
= new User ( ) ; Role role
= roleRepository
. findAllByRoleName ( roleName
) . get ( 0 ) ; user
. setUserName ( userName
) ; user
. setRoles ( new ArrayList < > ( ) ) ; user
. getRoles ( ) . add ( role
) ; userRepository
. save ( user
) ; return userRepository
. findAll ( ) ; } @RequestMapping ( "/adduserrole" ) public List
< User> addUserRole ( @RequestParam ( value
= "userName" ) String userName
, @RequestParam ( value
= "roleName" ) String roleName
) { User user
= userRepository
. findAllByUserName ( userName
) . get ( 0 ) ; Role role
= roleRepository
. findAllByRoleName ( roleName
) . get ( 0 ) ; if ( user
. getRoles ( ) == null
) { user
. setRoles ( new ArrayList < > ( ) ) ; } user
. getRoles ( ) . add ( role
) ; userRepository
. save ( user
) ; return userRepository
. findAll ( ) ; } @RequestMapping ( "/deleteuser" ) public List
< User> deleteUser ( @RequestParam ( value
= "userName" ) String userName
) { entityService
. deleteUser ( userName
) ; return userRepository
. findAll ( ) ; } @RequestMapping ( "/getauth" ) public Set
< Authority> getAuthority ( @RequestParam ( value
= "userName" ) String userName
) { Set
< Authority> authoritieSet
= new HashSet < > ( ) ; User user
= userRepository
. findAllByUserName ( userName
) . get ( 0 ) ; for ( Role role
: user
. getRoles ( ) ) { for ( Authority authority
: role
. getAuthorities ( ) ) { authoritieSet
. add ( authority
) ; } } return authoritieSet
; } @RequestMapping ( "/findallrole" ) public List
< Role> findAllRole ( ) { return roleRepository
. findAll ( ) ; } @RequestMapping ( "/addrole" ) public List
< Role> addRole ( @RequestParam ( value
= "roleName" ) String roleName
, @RequestParam ( value
= "authName" ) String authName
) { Role role
= new Role ( ) ; Authority authority
= authorityRepository
. findAllByAuthorityName ( authName
) . get ( 0 ) ; role
. setRoleName ( roleName
) ; role
. setAuthorities ( new ArrayList < > ( ) ) ; role
. getAuthorities ( ) . add ( authority
) ; roleRepository
. save ( role
) ; return roleRepository
. findAll ( ) ; } @RequestMapping ( "/addroleauth" ) public List
< Role> addRoleAuth ( @RequestParam ( value
= "roleName" ) String roleName
, @RequestParam ( value
= "authName" ) String authName
) { Role role
= roleRepository
. findAllByRoleName ( roleName
) . get ( 0 ) ; Authority authority
= authorityRepository
. findAllByAuthorityName ( authName
) . get ( 0 ) ; if ( role
. getAuthorities ( ) == null
) { role
. setAuthorities ( new ArrayList < > ( ) ) ; } role
. getAuthorities ( ) . add ( authority
) ; roleRepository
. save ( role
) ; return roleRepository
. findAll ( ) ; } @RequestMapping ( "/deleterole" ) public List
< Role> deleteRole ( @RequestParam ( value
= "roleName" ) String roleName
) { entityService
. deleteRole ( roleName
) ; return roleRepository
. findAll ( ) ; } @RequestMapping ( "/findallauth" ) public List
< Authority> findAllAuthority ( ) { return authorityRepository
. findAll ( ) ; } @RequestMapping ( "/addauth" ) public List
< Authority> addAuthority ( @RequestParam ( value
= "authName" ) String authName
) { Authority authority
= new Authority ( ) ; authority
. setAuthorityName ( authName
) ; authorityRepository
. save ( authority
) ; return authorityRepository
. findAll ( ) ; } @RequestMapping ( "/deleteauth" ) public List
< Authority> deletAuthority ( @RequestParam ( value
= "authName" ) String authName
) { entityService
. deleteAuthority ( authName
) ; return authorityRepository
. findAll ( ) ; }
}
五、運行效果
寫得函數(shù)有點多,這里挑選一部分來演示吧。
數(shù)據(jù)表 在程序運行之后,它會自動為我們在數(shù)據(jù)庫中創(chuàng)建5張表,其中包括3個實體對應的數(shù)據(jù)表以及2張映射表。
查詢操作 由于先前已經(jīng)進行了一些實驗,數(shù)據(jù)表中已經(jīng)有了少量的數(shù)據(jù),所以我們就現(xiàn)在演示查詢吧。 首先按照上文說的添加順序,先是權限的查詢。 接著是角色的查詢: 接著是用戶查詢: 最后,我們通過用戶名來查詢他擁有的權限。
增加角色操作 添加權限的操作很常規(guī)不做演示,添加用戶的操作和添加角色的操作差不多可以借鑒。
六、參考資料
https://www.cnblogs.com/hhhshct/p/9492741.html https://liuyanzhao.com/7913.html https://blog.csdn.net/lidai352710967/article/details/83509821 https://blog.csdn.net/H_Shun/article/details/78013017 https://blog.csdn.net/Just_learn_more/article/details/90665009 https://www.sojson.com/blog/295.html https://www.jianshu.com/p/6bbb5748ac83
總結
以上是生活随笔 為你收集整理的Spring Boot实现简单的用户权限管理(超详细版) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內容還不錯,歡迎將生活随笔 推薦給好友。