Należy wziąć pod uwagę następującą usługę (domyślnie transakcyjna). Gracz musi zawsze mieć jedno konto. Gracz bez co najmniej jednego odpowiadającego konta jest stanem błędu.Grails 2.4.4: Jak niezawodnie wycofywać w złożonej metodzie serwisowej.
class playerService {
def createPlayer() {
Player p new Player(name: "Stephen King")
if (!p.save()) {
return [code: -1, errors:p.errors]
}
Account a = new Account(type: "cash")
if (!a.save()) {
// rollback p !
return [code: -2, errors:a.errors]
}
// commit only now!
return [code: 0, player:p]
}
}
Widziałem tego wzorca przez doświadczonych Grails programistów, a kiedy mówię im, że jeśli powstanie na konto gracza nie z jakiegokolwiek powodu, to przyzwyczajenie wycofać odtwarzacz, a pozostawi DB w nieprawidłowy stan, patrzą na mnie jak na szaleńca, ponieważ grails radzi sobie z wycofywaniem gracza, ponieważ usługi są transakcyjne, prawda?
Tak więc, będąc facetem SQL, szukam sposobu na wywołanie wycofania w grails. Nie ma ani jednego. Według różnych postów, istnieją tylko 2 sposoby wymuszenia grails na wycofanie w usłudze:
- rzuca niesprawdzony wyjątek. Wiesz co to jest prawda?
- nie używać metod transakcyjnych usług lub adnotacji, użyj tego konstruktu:
.
DomainObject.withTransaction {status ->
//stuff
if (someError) {
status.setRollbackOnly()
}
}
1. wygeneruje niekontrolowany wyjątek
1,1 Więc musimy rzucać wyjątków czasu wykonywania cofnąć. Jest to dla mnie ok (lubię wyjątki), ale to nie żelu z deweloperami grails, którzy mamy, którzy przeglądają wyjątki jako powrót do Java i jest uncool. Oznacza to również, że musimy zmienić sposób, w jaki aplikacja obecnie korzysta z warstwy usługi.
1.2 Jeśli zostanie zgłoszony wyjątek, utracisz wartość p.errors - utracisz szczegóły sprawdzania poprawności.
1.3 Nasi nowi twórcy grails nie znają różnicy między niesprawdzonym a sprawdzonym wyjątkiem i nie wiedzą, jak odróżnić. To naprawdę niebezpieczne.
1.4. use .save (failOnError: true) Jestem wielkim fanem używania tego, ale nie jest on odpowiedni wszędzie. Czasami trzeba sprawdzić przyczynę przed pójściem dalej, nie wyrzucać wyjątku. Czy wyjątki, które generuje, zawsze są sprawdzane, zawsze niezaznaczone, czy też? To znaczy. czy nastąpi powrót do AWARII failOnError, bez względu na przyczynę? Nikt, o kogo nie pytałem, nie zna odpowiedzi na to, co jest niepokojące, oni używają ślepej wiary, aby uniknąć zepsutych/niespójnych DB.
1.5 Co dzieje się, gdy kontroler wywołuje usługę A, która wywołuje usługę B, a następnie usługę C. Usługa A musi wychwycić każdy wyjątek i zwrócić ładnie sformatowaną wartość zwracaną do kontrolera. Jeśli usługa C zgłasza wyjątek, który jest przechwytywany przez usługę A, usługa wycofuje transakcje Bs? Jest to bardzo ważne, aby wiedzieć, jak zbudować działającą aplikację.
UPDATE 1: Po wykonaniu niektórych testów wydaje się, że każdy wyjątek środowiska wykonawczego, nawet jeśli zostanie zgłoszony i przechwycony w niektórych niepowiązanych wywołaniach podrzędnych, spowoduje, że wszystko w obiekcie nadrzędnym zostanie wycofane. Jednak w sesji nadrzędnej nie jest łatwo zauważyć, że doszło do tego wycofania - musisz upewnić się, że jeśli zauważysz wyjątek, albo ponownie zgłosisz, albo zwrócisz uwagę rozmówcy, aby pokazać, że nie powiodło się w takiej sytuacji. sposób, w jaki wszystko inne zostanie wycofane.
2.withTransaction
2.1 Wydaje się, że jest to konstrukcja bazarowa. Jak mogę to nazwać i co mam przekazać dla parametru "status"? Co to jest "setRollbackOnly" dokładnie. Dlaczego nie nazywa się to tylko "wycofaniem". Co to jest "tylko" część? Jest związany z obiektem domeny, gdy twoja metoda może chcieć zaktualizować kilka różnych obiektów domeny.
2.2 Gdzie należy umieścić ten kod? W klasie DomainObject? W folderze źródłowym (tzn. Nie w usłudze lub kontrolerze?)? Bezpośrednio w kontrolerze? (nie chcemy duplikować logiki biznesowej w kontrolerach)
3. Idealna sytuacja.
3,1 ogólnym przypadku jest chcemy wszystko, co robimy w metodzie usług cofnąć, jeśli coś w tej metodzie usług mogę być zapisywane z dowolnego powodu, albo rzuca żadnego wyjątku dla jakiejkolwiek przyczyny (zaznaczone lub odznaczone).
3.2 Idealnie chciałbym, aby metody serwisowe "zawsze wycofywały się, chyba że wyraźnie wezwę commit", co jest najbezpieczniejszą strategią, ale nie jest to możliwe, wierzę.
Pytanie brzmi, jak osiągnąć idealną sytuację?
Czy wywołanie zapisu (failOnError: true) ZAWSZE wycofuje wszystko, niezależnie od przyczyny niepowodzenia? Nie jest to doskonałe, ponieważ osoba dzwoniąca nie jest łatwo wiedzieć, który obiekt domeny spowodował problem.
A może ludzie definiują wiele klas wyjątków, które podklasy runtimeException, a następnie jawnie łapią każdy z nich w kontrolerze, aby utworzyć odpowiednią odpowiedź? To jest stara metoda Java, a nasze groovy deweloperzy są pooh takie podejście ze względu na ilość kodu płyty kotła będziemy musieli napisać.
Jakie metody wykorzystują ludzie, aby to osiągnąć?
jest to pytanie dla mnie, postanowiłem nadrobić wszystko w kontrolerze i rzucać własne błędy, które są dziećmi RuntimeException. Widzę, że to jedyny sposób na stworzenie właściwej odpowiedzi. –