Notyfikacje po zapisie w bazie

0

Hej

Ogólnie to chce poinformować użytkownika, który zapisał się do newslettera. Mam tabelę **User | Produkt ** i gdy zostanie dodany do bazy dany produkt, użytkownik zostanie powiadomiony. Jak wysłać do użytkownika notyfikacje po dodaniu do bazy? Próbowałem z ProduktListener i adnotacją @PostPersist, ale nie mogę sobie poradzić z odczytem usera bo zawsze dostaje null.

@Entity
@EntityListeners(ProductListener.class)
public class Product{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String description; 
...

oraz

public class ProductListener(){

@Autowired
UserDao userDao;

@PostPersist
public void NotifyUser(Product product){
...
userDao,getOne(1L); //zawsze null 
...

Jak jest jakiś lepszy sposób a przede wszystkim działający to proszę o podpowiedź.

Dzięki za każdą pomoc

0

Pokaż kod tego UserDao. Ten, który zwraca Ci null. Zawsze przepychasz akurat 1L jako parametr, czy to tylko przykład? Jeśli nie, to co w takim razie przepychasz? Z czego i jak wyciągasz informację, którego użytkownika o czym notyfikować?

0
toJaMichal skomentował(a):

To tylko przykład. Bardziej chodzi o to, że w ogóle @Autowired nie działa w tej klasie.

W takim razie to trochę słaby przykład, bo sugeruje zupełnie co innego, niż dzieje się w rzeczywistości. Wyglądało to tak, jakby rezultat userDao.getOne(1L) miał zwracać nulla.

Obstawiam, że źródło problemów leży w tym, że ProductListener jest podpięty do encji przez adnotację @EntityListeners. JPA zapewne pod spodem woła sobie przez refleksje domyślny konstruktor (w tej chwili zgaduję, jakoś tak nie używałem dotąd JPA), a ponieważ z perspektywy Springa ten konstruktor wołany jest "na boku", nie ma mowy, by Spring mógł zająć się wstrzyknięciem instancji UserDao za Ciebie.

@jarekr000000 powiedziałby pewnie, żebyś nie powierzał robienia wszystkiego za Ciebie Springowi i pozbył się adnotacji, póki jeszcze możesz, a kod aplikacji odciął od frameworka. W sumie zawsze to jakiś sposób, utworzyć potrzebne instancje ręcznie, w konstruktorze klasy, żeby się nie rozjeżdżało jak użyjesz sobie instancji jakiegoś komponentu wyjętej spoza kontenera IoC.

Jak chcesz zostać przy @Service, @Component, @Repository, @Autowired i innych Springowych wspaniałościach, zawsze możesz odpuścić sobie podpinanie tego przez jakieś listenery JPA i rozsyłanie notyfikacji załatwić wyżej, np. na poziomie jakiegoś ProductService. Ten już pewnie będzie oznaczony adnotacją @Service, więc Spring zajmie się sam wstrzyknięciem czego trzeba i obejdziesz problem.

1
toJaMichal skomentował(a):

@superdurszlak: a jak powiadomic ten ProductSerwis, ze produkt ktorego szuka user zostal dodany do bazy?

W typowych rozwiązaniach typu Enterprise FizzBuzz, gdzie wszystko masz ładnie podzielone na warstwy itp., masz minimum trzy warstwy aplikacji:

  • warstwa kontrolerów, czyli wszystkie Twoje endpointy przez które świat zewnętrzny może dobijać się do aplikacji. Tutaj lądują klasy w stylu ProductController, które wystawiają jakieś tam metody zmapowane na konkretne URLe i metody HTTP
  • warstwa z jakąś tam logiką aplikacji, czyli jakimiś "wysokopoziomowymi" operacjami np. dodaj produkt do bazy i powiadom użytkowników zapisanych do newslettera. Tutaj lądują klasy takie jak ProductService.
  • warstwa odpowiadająca za komunikację z bazą danych (bądź dowolnym innym miejscem do odczytu i zapisu danych), czyli operacje "niskopoziomowe" - z grubsza rzecz ujmując, jeśli jakiś ProductService potrzebuje odczytać lub zapisać jakieś dane, powinien robić to nie bezpośrednio strzelając zapytaniami do bazy itp., a za pośrednictwem tej właśnie warstwy - np. poprzez UserDao czy ProductDao wystawiającymi jakieś operacje na bazie przez swoje interfejsy, albo trochę bardziej elegancko UserRepository czy ProductRepository realizujący Repository Pattern.

W przypadku takiego podejścia kontroler ProductController po wywołaniu jakiegoś POST blablabla/api/product odpali odpowiednią metodę z ProductService, aby dodać produkt do bazy, ta metoda doda produkt do bazy poprzez jakieś ProductDao / ProductRepository, a dodatkowo zajmie się notyfikacjami. Możesz nawet wrzucić logikę rozsyłania notyfikacji do jakiegoś jeszcze innego komponentu (np. NotificationService) i wykorzystać go w tej metodzie do rozesłania notyfikacji tak, by ukryć przed ProductService jak dokładnie to rozsyłanie notyfikacji jest realizowane - unikniesz sytuacji, w której jeśli więcej akcji będzie powodowało to czy tamto, w każdym miejscu będziesz kopiować lub przenosić logikę rozsyłania notyfikacji - szczególnie, gdy będzie bardziej skomplikowana i np. jeden user skonfiguruje sobie ustawienia tak, by w przypadku X dostawać powiadomienie mailem, a w przypadku Y SMS, a jeszcze inny by wszystko dostawać tylko w jakichś wewnętrznych powiadomieniach.

0

Co by nie było, że olałem temat zrobiłem to! Zapomniałem z tego wszystkiego odpisać... Korzystałem z JPARepository i troszkę to komplikowało sprawę, bo zanim doszedłem jak nadpisać metodę save to się zeszło... Ogólnie to miałem wcześniej już NotificationService, ale nie mogłem go podpiąć. Wygląda mniej więcej to tak:

Najpierw dodałem interfejs ProductCustomSave z metodą, którą będę nadpisywał

public interface ProductCustomSave<T> {
			  <S extends T> S save(S entity);
	```
Implementacja
```java
public class ProductCustomSaveImpl implements ProductCustomSave<Product> {

	@PersistenceContext
	EntityManager em;

	@Autowired
	UserDao userDao;
	
	@Autowired
	NotificationsService notificationsService;
	
	@Override
	@Transactional
	public <S extends Product> S save(S entity) {
		em.persist(entity);
		em.flush();
		List<User> subscribedUsers = userDao.getAllSubscribed(entity); 
		notificationsService.send(subscribedUsers);

		return entity;
	}

I na sam koniec dodałem do mojego Dao

public interface ProductDao extends JpaRepository<Product, Long>, ProductCustomSave<Product> {
//moje customowe metody

Dzięki za podpowiedź !

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