W poprzednim wpisie przedstawiłem sposób na redukcję kodu w encjach przy pomocy dziedziczenia i adnotacji @MappedSuperclass. Rozwiązanie to możemy również stosować aby tworzyć kod bardziej przenośny, który niewielkim kosztem można użyć w innych projektach.
Większość aplikacji webowych stosuje autoryzację opartą o role (ang. Role Based Access Control), w takich wypadkach mamy zazwyczaj encję User oraz Role, pierwszą odpowiedzialną za przetrzymywanie informacji o użytkowniku a druga nazwy ról. O ile role są obszarem stałym – zawsze mają nazwę – o tyle użytkownicy często mają różne wariacje i relacje. Ot choćby powiązanie konta użytkownika z firmą.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | // Copyright (C) 2009 Code-House // All rights reserved package org.code_house.security.domain; import java.util.Calendar; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.ManyToMany; import javax.persistence.MappedSuperclass; import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.code_house.domain.BaseEntity; /** * Klasa reprezentująca użytkownika. Powinna ona zostać nadpisana w docelowym * systemie poprzez plik orm.xml. * * @author Łukasz Dywicki. */ @MappedSuperclass public class SecurityUser extends BaseEntity { /** * Hasło użytkownika. */ @Column private String password; /** * Login użytkownika. */ @Column private String username; /** * Konto wygasło. */ @Column @Temporal (TemporalType.TIMESTAMP) private Calendar expire; /** * Grupy do których przynależy użytkownik. */ @ManyToMany (mappedBy = "users" ) private Set<Role> roles; /** * Konto zablokowane np. po 3 nieudanych próbach logowania. */ @Column private boolean locked; /** * Konto wyłączone np. przez administratora. */ @Column private boolean enabled; // Dalej gettery i settery } |
Rola ma niekompletną definicję relacji do klasy SecurityUser, którą należy uzupełnić o informacje na temat encji docelowej i definicji tabeli pośredniczącej.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // Copyright (C) 2009 Code-House // All rights reserved package org.code_house.security.domain; import javax.persistence.Entity; import javax.persistence.ManyToMany; import javax.persistence.Table; import org.code_house.domain.NamedEntity; /** * Klasa reprezentująca rolę. Może to być np ADMIN, USER bądź cokolwiek innego. * * @author Łukasz Dywicki luke@code-house.org */ @Entity @Table (name = "roles" ) public class Role extends NamedEntity { @ManyToMany @JoinTable (name = "users_roles" , joinColumns = @JoinColumn (name = "role_id" , referencedColumnName = "id" ) ) private Set<SecurityUser> users; // gettery i settery } |
Oto kawałek konfiguracji XML, która uzupełni metadane i pozwoli na uruchomienie kodu. Atrybut target-entity pozwala na określenie tabeli docelowej relacji podczas gdy element inverse-join-column uzupełnia definicję tabeli pośredniczącej.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <? xml version = "1.0" encoding = "UTF-8" ?> < entity-mappings version = "1.0" xsi:schemaLocation = "http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" > < package >org.code_house.security.domain</ package > < entity class = "Role" > < attributes > < many-to-many name = "users" target-entity = "org.code_house.management.domain.User" > < join-table > < inverse-join-column name = "user_id" referenced-column-name = "id" /> </ join-table > </ many-to-many > </ attributes > </ entity > < entity class = "Memento" > < attributes > < one-to-one name = "user" target-entity = "org.code_house.management.domain.User" > < join-column name = "user_id" /> </ one-to-one > </ attributes > </ entity > </ entity-mappings > |
Przykład ten można wykorzystać by uzupełniać relacje, których nie sposób odzwierciedlić w kodzie.