XML i Adnotacje – kod ogólnego przeznaczenia i JPA
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ą.
// 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.
// 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.
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.
Modified
