JPA - Integracja danych z wielu tabel

0

Witam,
potrzebuje teoretycznej porady, stosownego opisu do problemu, którego jeszcze sam nie zbyt dobrze umiem zdefiniować. Tworzę aplikację i w żaden sposób nie mogę powiązać danych z wielu tabel, tak by przy np. zapisie danych uwierzytelniających użytkownika, móc się potem do nich odwołać z nowej klasy. Tak, stosowny model danych istnieje ale umiem zapisać tylko dwie z tabel wykorzystując klasy encyjne i beany. Powiązałem sobie role i dane uwierzytelniające (z różnych tabel) za pomocą @SecondaryTable, okej. Pozostało mi jeszcze pięć tabel z danymi biznesowymi, które są w relacji @ManyToOne do tabeli z głównymi danymi użytkownika. Stosuje wzorzec fasada i endpointy.

public class Users implements Serializable
{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Integer userId;

    @Column(name = "login", unique = true)
    private String login;

    @Column(name = "password")
    private String password;

    @Column(name = "email", unique = true)
    private String email;
    
    @Column(name = "role_name", table = "roles", unique = false, nullable = true, updatable = true)
    private String roleName;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userId", fetch = FetchType.EAGER)
    private List<Qualifications> qualificationsCollection = new ArrayList<Qualifications>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userId", fetch = FetchType.EAGER)
    private List<HistoryWork> historyWorkCollection = new ArrayList<HistoryWork>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userId", fetch = FetchType.EAGER)
    private List<Education> educationCollection = new ArrayList<Education>();
     
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userId", fetch = FetchType.EAGER)
    private List<Trainings> trainingsCollection = new ArrayList<Trainings>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userId", fetch = FetchType.EAGER)
    private List<Workers> workersCollection = new ArrayList<Workers>();
...}
public class Trainings implements Serializable
{
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "id")
    private Integer id;
    @Column(name = "training_begin")
    @Temporal(TemporalType.DATE)
    private Date trainingBegin;
    @Column(name = "training_end")
    @Temporal(TemporalType.DATE)
    private Date trainingEnd;
    @Size(max = 500)
    @Column(name = "content_training")
    private String contentTraining;
    @Column(name = "training_time")
    private Short trainingTime;
    @JoinColumn(name = "user_id", referencedColumnName = "user_id")
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
...}
@Stateless
public class UsersFacade extends AbstractFacade<Users>
{
    @PersistenceContext(unitName = "System.PU")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager()
    {
        return em;
    }

    public UsersFacade()
    {
        super(Users.class);
    }
    
}
@Stateful
public class UsersEndpoint
{
    @EJB
    private UsersFacade usersFacade;

    //private Object user;
        
    public void addUser(Users user)
    {
        usersFacade.create(user);
    }
    public List<Users> getUsersList() 
    {
        return usersFacade.findAll();
    }
    public void addUserBeforeEditing(Users addedUser) 
    {
        usersFacade.edit(addedUser);
    }
    public Users downloadUserToEdit(Users user) 
    {
        Users wanted_user = usersFacade.find(user.getUserId());
        return wanted_user;
    }
    public void removeUser(Users user) 
    {
        usersFacade.remove(user);
    }

}
@SessionScoped
public class UsersController implements Serializable
{
    @EJB
    private UsersEndpoint usersEndpoint;
        
    private Users addedUser;
    
    public Users getAddedUser()
    {
        return addedUser;
    }
    public void setAddedUser(Users addedUser)
    {
        this.addedUser = addedUser;
    }

    public void addUser() 
    {
        if(addedUser == null) 
        {
            throw new IllegalArgumentException("Próba dodania nowego użytkownika z pominięciem formularza");
        }
        usersEndpoint.addUser(addedUser);
        
        addedUser = null;
    }
...}

Przedstawiam fragment kodu programu, jest prosty a ja pewnych rzeczy po prostu nie potrafię nazwać, gdyż stykam się z nimi po raz pierwszy.

0
  • złe nazwy klas, np. Users
  • "rozluźnij" powiązania między tabelami. Nie znam modelu, ale wszystko kręci się w okół użytkownika :/ Tyle kolekcji eagerowych zabiję aplikację, by pobrać, jednego użytkownika pobierasz od razu wszystko, mimo, że pewnie nie potrzebujesz większości !
  • zastosuj wzorzec DAO - jedna encja - jedna klasa dostępowa, która wykonuje operacje CRUDowe + jakieś customowe, a potem strzel service, który będzie posiadał przyjemniejsze API.

poza tym niezbyt rozumiem pytanie. Źle zaprojektowałeś model - tu lezy moim zdaniem klucz. Postaraj się wyodrębnić osobne byty (mam tu na myśli bardziej... bazodanowe podejście ;)) Taka jest moja opinia ;)

0

Najprościej chodzi mi o powiązanie krotek z wielu tabel na poziomie controlera bym nie musiał w modelu, encjach łączyć tabel w jedną wielką. Zaczyna się od encji, potem są fasady, które dziedziczą z klasy abstrakcyjnej z metodami typu: create(), potem do obsługi wchodzi endpoint, który przekazuje metody do controlera beanów, a ten z kolej jest wstrzykiwany @Inject do ziaren odpowiedzialnych za strony *.xhtml. Poznałem ten model na zajęciach jednak zaczynam żałować, że nie opracowałem prostszego. Promotor w ogóle się nie przydaje, a problem jest już na tyle złożony że bez przykładów nie dojdę metodą prób i błędów do rozwiązania.

Model danych: tabela users i roles są połączone @SecondaryTable, pozostałe tabele zawierają relacje do users @ManyToOne, zakładałem, że takie rozwiązanie wystarczy. Skoro dane są pobierane za każdym razem jak je zapisywać, odczytywać. Chciałem uniknąć tworzenia kilku osobnych klas dla każdej tabeli w różnych warstwach aplikacji, bo wydało mi się to bez sensu. Zresztą skąd aplikacja ma wiedzieć, która krotka na id do której w tabeli głównej users.

Przesyłam printscrean do zapoznania się modelem, dziękuje za pomoc.
Czekam na dalsze wskazówki :) Pozdrawiam.

0

Głupota nie boli, może zadałem złe pytanie... :P A więc czy muszę stosować łączenie tabel na poziomie encji by móc tworzyć beany i w widokach odpowiednie odwołania?

0

Cały ten "wzorzec" jest o kant d*y potłuc. 3 klasy żeby i tak wywołać em.persist/ em.find/ ... chyba po to żeby móc narysować na kartce ładny diagram. Ja tam bym radził spróbować najpierw napisać to najbardziej kompaktowo jak tylko potrafisz, a potem w razie potrzeby rozwijać architekturę.

Najprościej chodzi mi o powiązanie krotek z wielu tabel na poziomie controlera bym nie musiał w modelu, encjach łączyć tabel w jedną wielką.

  • nie wiem czy dobrze zrozumiałem, ale chyba próbujesz manualnie budować graf encji. Przecież od tego jest JPA, wykonujesz zapytanie i masz go od razu.
0

Od dawna dostaje odpowiedzi, że wystarczy zrobić coś "po prostu i wyjdzie samo, bo to oczywiste". Wzorzec nie jest taki złożony, raczej standardowy na moje oko :) Wykładowcy z Politechniki myślę wiedzą co podają. Nie widać wszystkiego, bo wrzuciłem tylko część kodu ale po prostu nie chciałem "niegrzecznie" wywalać na forum całego projektu :P

Dobra mniejsza z tym, pewnych rzeczy muszę i tak nauczyć się sam. W tym momencie jednak dochodzę do wniosku, że model encji jest zbyt prosty, nie stosowałem dodatkowych adnotacji, złożyłem po prostu zwyczajnie najprostsze encje. Teraz się zastanawiam, czemu przy zapisie Workera dostaje komunikat, że brakuje id od Usera. No nic dziękuje za chęci, ale chyba zadałem zbyt mało konkretne pytanie.

Dzięki za pomoc.

0
Pangeon napisał(a):

Teraz się zastanawiam, czemu przy zapisie Workera dostaje komunikat, że brakuje id od Usera.

Nie wiem czy o to chodzi, ale według specyfikacji JPA po wykonaniu persist trzeba jeszcze dać refresh żeby dostać id encji.

0

Tak właśnie, chodzi mi o możliwość zapisu danych w osobnych formularzach, widokach tak by wszystko zespajało się jedno. By tworzyły się dane w bazie na podstawie zdefiniowanych powiązań. Nie wiem czemu ale jest to dla mnie problem nie do pokonania, za każdym razem trafiam na przeszkodę. Trwa to długo, bo sam już nie wiem czy to encje mam w jakiś sposób łączyć czy tworzyć odpowiednie beany, controlery, fasady. Rozumiem pojedynczy proces zapisu, mam za to problem z wieloma klasami ogarnąłem tylko @SecondaryTable, a chodzi mi zapis w wielu tabelach... masakra, nie mam siły :(

1 użytkowników online, w tym zalogowanych: 0, gości: 1