I'm trying to implement transactions with spring-data-r2dbc repositories in combination with the TransactionalDatabaseClient as such:
class SongService(
private val songRepo: SongRepo,
private val databaseClient: DatabaseClient
){
private val tdbc = databaseClient as TransactionalDatabaseClient
...
...
fun save(song: Song){
return tdbc.inTransaction{
songRepo
.save(mapRow(song, albumId)) //Mapping to a row representation
.delayUntil { savedSong -> tdbc.execute.sql(...).fetch.rowsUpdated() } //saving a many to many relation
.map(::mapSong) //Mapping back to actual song and retrieve the relationship data.
}
}
}
I currently have a config class (annotated with #Configuration and #EnableR2dbcRepositories) that extends from AbstractR2dbcConfiguration. In here I override the databaseClient method to return a TransactionalDatabaseClient. This should be the same instance as in the SongService class.
When running the code in a test with just subscribing and printing, I get org.springframework.transaction.NoTransactionException: ReactiveTransactionSynchronization not active and the relationship data is not returned.
When using project Reactors stepverifier though, i get java.lang.IllegalStateException: Connection is closed. Also in this case, the relationship data is not returned.
Just for the record, I have seen https://github.com/spring-projects/spring-data-r2dbc/issues/44
Here is a working Java example:
#Autowired TransactionalDatabaseClient txClient;
#Autowired Mono<Connection> connection;
//You Can also use: #Autowired Mono<? extends Publisher> connectionPublisher;
public Flux<Void> example {
txClient.enableTransactionSynchronization(connection);
// Or, txClient.enableTransactionSynchronization(connectionPublisher);
Flux<AuditConfigByClub> audits = txClient.inTransaction(tx -> {
txClient.beginTransaction();
return tx.execute().sql("SELECT * FROM audit.items")
.as(Item.class)
.fetch()
.all();
}).doOnTerminate(() -> {
txClient.commitTransaction();
});
txClient.commitTransaction();
audits.subscribe(item -> System.out.println("anItem: " + item));
return Flux.empty()
}
I just started reactive so not too sure what I'm doing with my callbacks haha. But I decided to go with TransactionalDatabaseClient over DatabaseClient or Connection since I'll take all the utility I can get while R2dbc is in its current state.
In your code did you actually instantiate a Connection object? If so I think you would have done it in your configuration. It can be utilized throughout the app the same as DatabaseClient, but it is slightly more intricate.
If not:
#Bean
#Override // I also used abstract config
public ConnectionFactory connectionFactory() {
...
}
#Bean
TransactionalDatabaseClient txClient() {
...
}
//TransactionalDatabaseClient will take either of these as arg in
//#enableTransactionSynchronization method
#Bean
public Publisher<? extends Connection> connectionPublisher() {
return connectionFactory().create();
}
#Bean
public Mono<Connection> connection() {
return = Mono.from(connectionFactory().create());
}
If you are having problems translating to Kotlin, there is an alternative way to enable synchronization that could work:
// From what I understand, this is a useful way to move between
// transactions within a single subscription
TransactionResources resources = TransactionResources.create();
resources.registerResource(Resource.class, resource);
ConnectionFactoryUtils
.currentReactiveTransactionSynchronization()
.subscribe(currentTx -> sync.registerTransaction(Tx));
Hope this translates well for Kotlin.
Related
I have a class :
data class Stam(#SerializedName("blabla") val blabla: String = "")
I want to do gson.fromJson("{\"blabla\":null}", Stam::class.java)
However, it will fail because blabla is not nullable.
I want to make it so if gson failed to deserialize some variable, it will take the default value I give it.
How to achieve that?
I don't think it is possible with GSON, this is one of the reasons why kotlinx.serialization library was created. With this library it is fairly easy:
#Serializable
data class Stam(#SerialName("blabla") val blabla: String = "") //actually, #SerialName may be omitted if it is equal to field name
Json { coerceInputValues = true }.decodeFromString<Stam>("{\"blabla\":null}")
I wouldn't say it is not possible in Gson, but Gson is definitely not the best choice:
Gson has no mention on Kotlin, its runtime and specifics, so one is better to use a more convenient and Kotlin-aware tool. Typical questions here are: how to detect a data class (if it really matters, can be easily done in Kotlin), how to detect non-null parameters and fields in runtime, etc.
Data classes in Kotlin seem to provide a default constructor resolvable by Gson therefore Gson can invoke it (despite it can instantiate classes instances without constructors using unsafe mechanics) delegating to the "full-featured" constructor with the default arguments. The trick here is removing null-valued properties from input JSON so Gson would keep "default-argumented" fields unaffected.
I do Java but I do believe the following code can be converted easily (if you believe Gson is still a right choice):
final class StripNullTypeAdapterFactory
implements TypeAdapterFactory {
// The rule to check whether this type adapter should be applied.
// Externalizing the rule makes it much more flexible.
private final Predicate<? super TypeToken<?>> isClassSupported;
private StripNullTypeAdapterFactory(final Predicate<? super TypeToken<?>> isClassSupported) {
this.isClassSupported = isClassSupported;
}
static TypeAdapterFactory create(final Predicate<? super TypeToken<?>> isClassSupported) {
return new StripNullTypeAdapterFactory(isClassSupported);
}
#Override
#Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !isClassSupported.test(typeToken) ) {
return null;
}
// If the type is supported by the rule, get the type "real" delegate
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new StripNullTypeAdapter<>(delegate);
}
private static final class StripNullTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegate;
private StripNullTypeAdapter(final TypeAdapter<T> delegate) {
this.delegate = delegate;
}
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegate.write(out, value);
}
#Override
public T read(final JsonReader in) {
// Another disadvantage in using Gson:
// the null-stripped object must be buffered into memory regardless how big it is.
// So it may generate really big memory footprints.
final JsonObject buffer = JsonParser.parseReader(in).getAsJsonObject();
// Strip null properties from the object
for ( final Iterator<Map.Entry<String, JsonElement>> i = buffer.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<String, JsonElement> property = i.next();
if ( property.getValue().isJsonNull() ) {
i.remove();
}
}
// Now there is no null values so Gson would only use properties appearing in the buffer
return delegate.fromJsonTree(buffer);
}
}
}
Test:
public final class StripNullTypeAdapterFactoryTest {
private static final Collection<Class<?>> supportedClasses = ImmutableSet.of(Stam.class);
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
// I don't know how easy detecting data classes and non-null parameters is
// but since the rule is externalized, let's just lookup it
// in the "known classes" registry
.registerTypeAdapterFactory(StripNullTypeAdapterFactory.create(typeToken -> supportedClasses.contains(typeToken.getRawType())))
.create();
#Test
public void test() {
final Stam stam = gson.fromJson("{\"blabla\":null}", Stam.class);
// The test is "green" since
Assertions.assertEquals("", stam.getBlabla());
}
}
I still think Gson is not the best choice here.
I found examples on the Internet but this doesn't give me a full understanding. Standard CRUD when using WebFlux.
Router:
#Configuration
public class PersonRouter {
#Bean
public RouterFunction<ServerResponse> route(PersonHandler handler) {
return RouterFunctions
.route(GET("/getAllPersons").and(accept(MediaType.APPLICATION_JSON)), handler::findAll)
.andRoute(GET("/getPerson/{id}").and(accept(MediaType.APPLICATION_STREAM_JSON)), handler::findById)
.andRoute(POST("/createPerson").and(accept(MediaType.APPLICATION_JSON)), handler::save)
.andRoute(DELETE("/deletePerson/{id}").and(accept(MediaType.APPLICATION_JSON)), handler::delete);
}
}
Handler:
#Component
public class PersonHandler {
private final PersonService personService;
public PersonHandler(PersonService personService) {
this.personService = personService;
}
public Mono<ServerResponse> findById(ServerRequest request) {
String id = request.pathVariable("id");
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(personService.getById(id), Person.class);
}
public Mono<ServerResponse> findAll(ServerRequest request) {
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(personService.getAll(), Person.class);
}
public Mono<ServerResponse> save(ServerRequest request) {
final Mono<Person> person = request.bodyToMono(Person.class);
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(fromPublisher(person.flatMap(personService::save), Person.class));
}
public Mono<ServerResponse> delete(ServerRequest request) {
String id = request.pathVariable("id");
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(personService.delete(id), Void.class);
}
}
Repository:
#Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
}
Service:
#Service
#Transactional
#AllArgsConstructor
public class PersonService {
private final PersonRepository personRepository;
public Flux<Person> getAll() {
return personRepository.findAll().switchIfEmpty(Flux.empty());
}
public Mono<Person> getById(final String id) {
return personRepository.findById(id);
}
public Mono update(final String id, final Person person) {
return personRepository.save(person);
}
public Mono save(final Person person) {
return personRepository.save(person);
}
public Mono delete(final String id) {
final Mono<Person> dbPerson = getById(id);
if (Objects.isNull(dbPerson)) {
return Mono.empty();
}
return getById(id).switchIfEmpty(Mono.empty()).filter(Objects::nonNull).flatMap(personToBeDeleted -> personRepository
.delete(personToBeDeleted).then(Mono.just(personToBeDeleted)));
}
}
I understand everything except the save and update methods. I don't understand why we use flatMap in this situation.
Why is this so, and how can I write the implementation of the update method in my Handler.
Updated
Let's see the method save() in Handler
public Mono<ServerResponse> save(ServerRequest request) {
final Mono<Person> person = request.bodyToMono(Person.class);
return ok()
.contentType(MediaType.APPLICATION_JSON)
.body(fromPublisher(person.flatMap(personService::save), Person.class));
}
I think the fact is that we have already received:
final Mono<Person> person = request.bodyToMono(Person.class);
and then we do:
personService::save
As a result, we get Mono< Mono< Person>>
flatMap is just like map, except that it unpacks the return value of the lambda given if the value is itself contained in a Publisher<T>. In our case, the personService.save(T) method returns a Mono<T>. If we’d used map instead of flatMap(T), we’d have a Mono< Mono< T>>, when what we really want is a Mono<T>. We can cleanly solve this problem using flatMap.
Am I right or is this statement wrong?
why you need flatMap.
These are my ideas, the answer varies depending on whether you it works on Mono or Flux.
1.
javadoc of method map and flatMap shows their usage:
map: Transform the item emitted by this {#link Mono} by applying a synchronous function to it.
flatMap: Transform the item emitted by this {#link Mono} asynchronously, returning the value emitted by another {#link Mono} (possibly changing the value type).
That says, considering flatMap and map as a pipeline with input and output, you use map when the output is the same item , otherwise, use flatMap. Check this:
public Mono<ServerResponse> influCRUD(ServerRequest req) {
return req.bodyToMono(S.class) // the pipline begins with S class.
.map(s -> {s.setF1(f1); s.setF2(f2); return s;}) // the pipeline has the same intput and output, i.e. object s, you use map.
.flatMap(s -> webClient // the pipeline has S input, and T output, you use flatMap
.post()
.uri(uri)
.body(BodyInserters.fromObject(s))
.retrive()
.bodyToMono(T.class)
).flatMap(t -> ServerResponse // now the pipeline changes again, you use flatMap.
.ok()
.contentType()
.body(BodyInserters.fromObject(t))
);
}
it deserves to mention that map can have different object as output as well.
flatMap processes every items
the above reason is usefull for Mono producer. For Flux, flatMap processes every items, while map processes all items (or one item). this is the same as they are in lambda. In case you want to process every item, you use flatMap.
flatMap takes off one layer of Mono for you.
Look at their declaration:
<R> Mono<R> map(Function<? super T, ? extends R> mapper)
and
<R> Mono<R> flatMap(Function<? super T, ? extends Mono<? extends R>> transformer)
Function does nothing but a -> b, when b is the output of another Producer/Subsciber (this is very likely when you use reactive programming) like the webClient part in the former example, it is in the form of Mono or Flux. By using flatMap, it returns Mono<R> for you, where map returns Mono<Mono<R>>, as they are stated in the function declaration.
I am a beginner in reative programming too, more than welcome to correct this.
I am using Webflux in Spring Boot 2.0.3.RELEASE to create REST API. With that implementation, I customize and use the webSessionManager as below.
#EnableWebFluxSecurity
#Configuration
class SecurityConfiguration {
#Bean
fun webSessionManager(): WebSessionManager {
return DefaultWebSessionManager().apply {
sessionIdResolver = HeaderWebSessionIdResolver().apply {
headerName = "X-Sample"
}
sessionStore = InMemoryWebSessionStore()
}
}
// ...
}
And in order to test the REST API, I created a test code as follows. (addUser and signin are extension functions.)
#RunWith(SpringRunner::class)
#SpringBootTest
#AutoConfigureWebTestClient
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
class UserTests {
#Autowired
private lateinit var client: WebTestClient
#Test
fun testGetUserInfo() {
client.addUser(defaultUser)
val sessionKey = client.signin(defaultUser)
client.get().uri(userPath)
.header("X-Sample", sessionKey)
.exchange()
.expectStatus().isOk
.expectBody()
.jsonInStrict("""
{
"user": {
"mail_address": "user#example.com"
}
}
""".trimIndent())
}
// ...
}
The test failed. It is refused by authorization. However, if I start the server and run it from curl it will succeed in the authorization.
After investigating the cause, it turned out that org.springframework.test.web.reactive.server.AbstractMockServerSpec set webSessionManager to DefaultWebSessionManager. Default is used, not the webSessionManager I customized. For this reason, it could not get the session ID.
AbstractMockServerSpec.java#L41
AbstractMockServerSpec.java#L72-L78
How can I change the webSessionManager of AbstractMockServerSpec?
Also, I think that it is better to have the following implementation, what do you think?
abstract class AbstractMockServerSpec<B extends WebTestClient.MockServerSpec<B>>
implements WebTestClient.MockServerSpec<B> {
// ...
private WebSessionManager sessionManager = null;
// ...
#Override
public WebTestClient.Builder configureClient() {
WebHttpHandlerBuilder builder = initHttpHandlerBuilder();
builder.filters(theFilters -> theFilters.addAll(0, this.filters));
if (this.sessionManager != null) {
builder.sessionManager(this.sessionManager);
}
this.configurers.forEach(configurer -> configurer.beforeServerCreated(builder));
return new DefaultWebTestClientBuilder(builder);
}
// ...
}
Spring Framework's AbstractMockServerSpec is providing a method to customize the WebSessionManager already.
Thanks for opening SPR-17094, this problem will be solved with that ticket - the AbstractMockServerSpec is already looking into the application context for infrastructure bits, this should check for a WebSessionManager as well.
I have this abstract class:
public abstract class Accessor<T extends Id, U extends Value>
{
public U find(T id)
{
// let's say
return getHelper().find(id);
}
}
And an implementation:
public FooAccessor extends Accessor<FooId,Foo>
{
public Helper getHelper
{
// ...
return helper;
}
}
And I would like to mock the calls to FooAccessor.find.
This:
#MockClass(realClass=FooAccessor.class)
static class MockedFooAccessor
{
public Foo find (FooId id)
{
return new Foo("mocked!");
}
}
will fail with this error:
java.lang.IllegalArgumentException: Matching real methods not found for the following mocks of MockedFooAccessor:
Foo find (FooId)
and I understand why... but I don't see how else I could do it.
Note: yes, I could mock the getHelper method, and get what I want; but this is more a question to learn about JMockit and this particular case.
The only way around this I have found is to use fields
#Test
public void testMyFooMethodThatCallsFooFind(){
MyChildFooClass childFooClass = new ChildFooClass();
String expectedFooValue = "FakeFooValue";
new NonStrictExpectations(){{
setField(childFooClass, "fieldYouStoreYourFindResultIn", expectedFooValue);
}};
childFooClass.doSomethingThatCallsFind();
// if your method is protected or private you use Deencapsulation class
// instead of calling it directly like above
Deencapsulation.invoke(childFooClass, "nameOfFindMethod", argsIfNeededForFind);
// then to get it back out since you used a field you use Deencapsulation again to pull out the field
String actualFoo = Deencapsulation.getField(childFooClass, "nameOfFieldToRunAssertionsAgainst");
assertEquals(expectedFooValue ,actualFoo);
}
childFooClass doesn't need to be mocked nor do you need to mock the parent.
Without more knowledge of your specific case this strategy has been the best way for me to leverage jMockit Deencapsulation makes so many things possilbe to test without sacrificing visibility. I know this doesn't answer the direct question but I felt you should get something out of it. Feel free to downvote and chastise me community.
Honestly, I do not find it in any way different from mocking regular classes. One way to go is to tell JMockit to mock only the find method and use Expectations block to provide alternate implementation. Like this:
abstract class Base<T, U> {
public U find(T id) {
return null;
}
}
class Concrete extends Base<Integer, String> {
public String work() {
return find(1);
}
}
#RunWith(JMockit.class)
public class TestClass {
#Mocked(methods = "find")
private Concrete concrete;
#Test
public void doTest() {
new NonStrictExpectations() {{
concrete.find((Integer) withNotNull());
result = "Blah";
}}
assertEquals("Blah", concrete.work());
}
}
Hope it helps.
I have this wcf method
Profile GetProfileInfo(string profileType, string profileName)
and a business rule:
if profileType is "A" read from database.
if profileType is "B" read from xml file.
The question is: how to implement it using a dependency injection container?
Let's first assume that you have an IProfileRepository something like this:
public interface IProfileRepository
{
Profile GetProfile(string profileName);
}
as well as two implementations: DatabaseProfileRepository and XmlProfileRepository. The issue is that you would like to pick the correct one based on the value of profileType.
You can do this by introducing this Abstract Factory:
public interface IProfileRepositoryFactory
{
IProfileRepository Create(string profileType);
}
Assuming that the IProfileRepositoryFactory has been injected into the service implementation, you can now implement the GetProfileInfo method like this:
public Profile GetProfileInfo(string profileType, string profileName)
{
return this.factory.Create(profileType).GetProfile(profileName);
}
A concrete implementation of IProfileRepositoryFactory might look like this:
public class ProfileRepositoryFactory : IProfileRepositoryFactory
{
private readonly IProfileRepository aRepository;
private readonly IProfileRepository bRepository;
public ProfileRepositoryFactory(IProfileRepository aRepository,
IProfileRepository bRepository)
{
if(aRepository == null)
{
throw new ArgumentNullException("aRepository");
}
if(bRepository == null)
{
throw new ArgumentNullException("bRepository");
}
this.aRepository = aRepository;
this.bRepository = bRepository;
}
public IProfileRepository Create(string profileType)
{
if(profileType == "A")
{
return this.aRepository;
}
if(profileType == "B")
{
return this.bRepository;
}
// and so on...
}
}
Now you just need to get your DI Container of choice to wire it all up for you...
Great answer by Mark, However the solution given is not Abstract factory but the implementation of Standard Factory pattern. Please check how Marks classes fit in the Standard Factory Pattern UML diagram. Click here to see above classes applied to Factory pattern UML
Since in Factory pattern, the factory is aware of the concrete classes, we can make the code of the ProfileRepositoryFactory much simpler like below. The problem with injecting the different repositories to factory is that you have more code changes every time you add a new concrete type. With below code you only have to update the switch to include new concrete class
public class ProfileRepositoryFactory : IProfileRepositoryFactory
{
public IProfileRepository Create(string profileType)
{
switch(profileType)
{
case "A":
return new DatabaseProfileRepository();
case "B":
return new XmlProfileRepository();
}
}
}
Abstract Factory is more advanced pattern used for creating families of related or dependent objects without specifying their concrete classes. The UML class diagram available here explains it well.