Aktualizacja: 04.10.2015. Zazwyczaj nie robię już żadnego z tych wierszy. Możesz przeprowadzić selektywną reprezentację JSON znacznie bardziej elegancko dzięki adnotacjom. Zobacz tę gist.
Spędziłem lepszą część całego dnia próbując rozgryźć to dla mojego przypadku 3-warstwowych obiektów zagnieżdżonych i właśnie w końcu go przybiłem. Oto moja sytuacja:
Konta (tj użytkowników) --1tomany -> Role --1tomany -> widoki (użytkownik może zobaczyć)
(. Te klasy POJO są wklejane na samym dole)
chciałem kontroler, aby powrócić do obiektu tak:
[ {
"id" : 3,
"email" : "[email protected]",
"password" : "sdclpass",
"org" : "Super-duper Candy Lab",
"role" : {
"id" : 2,
"name" : "ADMIN",
"views" : [ "viewPublicReports", "viewAllOrders", "viewProducts", "orderProducts", "viewOfferings", "viewMyData", "viewAllData", "home", "viewMyOrders", "manageUsers" ]
}
}, {
"id" : 5,
"email" : "[email protected]",
"password" : "stereopass",
"org" : "Stereolab",
"role" : {
"id" : 1,
"name" : "USER",
"views" : [ "viewPublicReports", "viewProducts", "orderProducts", "viewOfferings", "viewMyData", "home", "viewMyOrders" ]
}
}, {
"id" : 6,
"email" : "[email protected]",
"password" : "ukmedpass",
"org" : "University of Kentucky College of Medicine",
"role" : {
"id" : 2,
"name" : "ADMIN",
"views" : [ "viewPublicReports", "viewAllOrders", "viewProducts", "orderProducts", "viewOfferings", "viewMyData", "viewAllData", "home", "viewMyOrders", "manageUsers" ]
}
} ]
Kluczową kwestią jest uświadomienie sobie, że wiosna nie tylko robić to automatycznie wszystko dla Ciebie. Jeśli po prostu poprosić go, aby powrócić element konta bez wykonywania prac zagnieżdżonych obiektów, można jedynie dostać:
{
"id" : 6,
"email" : "[email protected]",
"password" : "ukmedpass",
"org" : "University of Kentucky College of Medicine",
"role" : null
}
Więc, po pierwsze, stworzyć swój 3-tabeli SQL JOIN kwerendy i upewnij się, że uzyskanie wszystkie potrzebne dane. Oto mój, jak się pojawia w moim kontrolerze:
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@RequestMapping("/accounts")
public List<Account> getAllAccounts3()
{
List<Account> accounts = jdbcTemplate.query("SELECT Account.id, Account.password, Account.org, Account.email, Account.role_for_this_account, Role.id AS roleid, Role.name AS rolename, role_views.role_id, role_views.views FROM Account JOIN Role on Account.role_for_this_account=Role.id JOIN role_views on Role.id=role_views.role_id", new AccountExtractor() {});
return accounts;
}
Pamiętaj, że ŁĄCZĘ 3 stoły. Teraz utwórz klasę RowSetExtractor, aby połączyć zagnieżdżone obiekty. Powyższe przykłady pokazują 2-warstwowe zagnieżdżanie ... ten idzie o krok dalej i robi 3 poziomy. Zwróć uwagę, że muszę również zachować obiekt drugiej warstwy na mapie.
public class AccountExtractor implements ResultSetExtractor<List<Account>>{
@Override
public List<Account> extractData(ResultSet rs) throws SQLException, DataAccessException {
Map<Long, Account> accountmap = new HashMap<Long, Account>();
Map<Long, Role> rolemap = new HashMap<Long, Role>();
// loop through the JOINed resultset. If the account ID hasn't been seen before, create a new Account object.
// In either case, add the role to the account. Also maintain a map of Roles and add view (strings) to them when encountered.
Set<String> views = null;
while (rs.next())
{
Long id = rs.getLong("id");
Account account = accountmap.get(id);
if(account == null)
{
account = new Account();
account.setId(id);
account.setPassword(rs.getString("password"));
account.setEmail(rs.getString("email"));
account.setOrg(rs.getString("org"));
accountmap.put(id, account);
}
Long roleid = rs.getLong("roleid");
Role role = rolemap.get(roleid);
if(role == null)
{
role = new Role();
role.setId(rs.getLong("roleid"));
role.setName(rs.getString("rolename"));
views = new HashSet<String>();
rolemap.put(roleid, role);
}
else
{
views = role.getViews();
views.add(rs.getString("views"));
}
views.add(rs.getString("views"));
role.setViews(views);
account.setRole(role);
}
return new ArrayList<Account>(accountmap.values());
}
}
a to daje pożądany wynik. POJO poniżej dla odniesienia. Zwróć uwagę na widoki zestawów @ElementCollection w klasie Role. To właśnie automatycznie generuje tabelę widoków ról, do której odwołuje się zapytanie SQL. Wiedząc, że tabela istnieje, jej nazwa i nazwy pól są kluczowe dla prawidłowego uzyskania zapytania SQL. Czuje się źle, wiedząc, że ... wydaje się, że powinno to być bardziej zautomatyzowane - czyż nie jest to tym, czym jest Wiosna? ... ale nie mogłem wymyślić lepszego sposobu. Musisz wykonać pracę ręcznie w tym przypadku, o ile wiem.
@Entity
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
@Column(unique=true, nullable=false)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String org;
private String phone;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "roleForThisAccount") // @JoinColumn means this side is the *owner* of the relationship. In general, the "many" side should be the owner, or so I read.
private Role role;
public Account() {}
public Account(String email, String password, Role role, String org)
{
this.email = email;
this.password = password;
this.org = org;
this.role = role;
}
// getters and setters omitted
}
@Entity
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id; // required
@Column(nullable = false)
@Pattern(regexp="(ADMIN|USER)")
private String name; // required
@Column
@ElementCollection(targetClass=String.class)
private Set<String> views;
@OneToMany(mappedBy="role")
private List<Account> accountsWithThisRole;
public Role() {}
// constructor with required fields
public Role(String name)
{
this.name = name;
views = new HashSet<String>();
// both USER and ADMIN
views.add("home");
views.add("viewOfferings");
views.add("viewPublicReports");
views.add("viewProducts");
views.add("orderProducts");
views.add("viewMyOrders");
views.add("viewMyData");
// ADMIN ONLY
if(name.equals("ADMIN"))
{
views.add("viewAllOrders");
views.add("viewAllData");
views.add("manageUsers");
}
}
public long getId() { return this.id;}
public void setId(long id) { this.id = id; };
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public Set<String> getViews() { return this.views; }
public void setViews(Set<String> views) { this.views = views; };
}
Powyżej fragment kodu jest uszkodzony. Jest więcej zamków zamykających niż otwierających. (4 vs 3) – fivedogit
@fivedogit - zaktualizowano - TY- mam nadzieję, że uznałeś to za przydatne – nclord