From 8396c33b4215639b871454d223dc6b1feb5de1f2 Mon Sep 17 00:00:00 2001 From: Nikolche Mihajlovski Date: Thu, 21 Dec 2017 20:23:35 +0100 Subject: [PATCH] #153 Serializing the result into a HTTP response body inside tx. --- .../src/main/resources/rapidoid-classes.txt | 3 + .../org/rapidoid/http/AbstractHttpServer.java | 4 +- .../main/java/org/rapidoid/http/FastHttp.java | 2 +- .../main/java/org/rapidoid/http/RespBody.java | 32 +++++++++ .../AbstractDecoratingHttpHandler.java | 39 ++++------- .../org/rapidoid/http/handler/TxUtil.java | 47 ++++++++++++++ .../java/org/rapidoid/http/impl/ReqImpl.java | 32 ++++----- .../org/rapidoid/http/impl/RespBodyBytes.java | 43 ++++++++++++ .../java/org/rapidoid/http/impl/RespImpl.java | 27 ++++---- .../rapidoid/http/impl/lowlevel/HttpIO.java | 3 +- .../http/impl/lowlevel/LowLevelHttpIO.java | 4 +- .../http/processor/AbstractHttpProcessor.java | 4 +- .../http/processor/NotFoundHttpProcessor.java | 6 +- .../org/rapidoid/jpa/JPALazyEntitiesTest.java | 65 +++++++++++++++++++ .../src/test/java/org/rapidoid/jpa/Movie.java | 9 ++- .../src/test/java/org/rapidoid/jpa/Tag.java | 59 +++++++++++++++++ .../testLazyEntitiesInTx/GET_movies | 8 +++ .../testLazyEntitiesWithoutTx/GET_movies | 8 +++ 18 files changed, 328 insertions(+), 67 deletions(-) create mode 100644 rapidoid-http-fast/src/main/java/org/rapidoid/http/RespBody.java create mode 100644 rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/TxUtil.java create mode 100644 rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespBodyBytes.java create mode 100644 rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/JPALazyEntitiesTest.java create mode 100644 rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Tag.java create mode 100644 rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesInTx/GET_movies create mode 100644 rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesWithoutTx/GET_movies diff --git a/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt b/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt index c3ab58b526..495f0b4996 100644 --- a/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt +++ b/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt @@ -399,6 +399,7 @@ org.rapidoid.http.handler.PredefinedResponseHandler org.rapidoid.http.handler.ResourceHttpHandler org.rapidoid.http.handler.StaticHttpHandler org.rapidoid.http.handler.StaticResourcesHandler +org.rapidoid.http.handler.TxUtil org.rapidoid.http.Headers org.rapidoid.http.HTTP org.rapidoid.http.HttpClient @@ -434,6 +435,7 @@ org.rapidoid.http.impl.MVCModelImpl org.rapidoid.http.impl.PathPattern org.rapidoid.http.impl.RapidoidReqInfo org.rapidoid.http.impl.ReqImpl +org.rapidoid.http.impl.RespBodyBytes org.rapidoid.http.impl.RespImpl org.rapidoid.http.impl.ResponseRenderer org.rapidoid.http.impl.RouteCacheConfig @@ -452,6 +454,7 @@ org.rapidoid.http.Req org.rapidoid.http.ReqHandler org.rapidoid.http.ReqRespHandler org.rapidoid.http.Resp +org.rapidoid.http.RespBody org.rapidoid.http.REST org.rapidoid.http.RESTClient org.rapidoid.http.RESTResultMapper diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/AbstractHttpServer.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/AbstractHttpServer.java index 4ef864abb8..c02a16ce65 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/AbstractHttpServer.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/AbstractHttpServer.java @@ -7,9 +7,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java index 82d634f22a..04397da358 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java @@ -291,7 +291,7 @@ private void internalServerError(final Channel channel, final boolean isKeepAliv MediaType contentType = req != null ? req.contentType() : HttpUtils.getDefaultContentType(); HttpResponseRenderer jsonRenderer = Customization.of(req).jsonResponseRenderer(); - byte[] body = HttpUtils.responseToBytes(req, INTERNAL_SERVER_ERROR, contentType, jsonRenderer); + RespBody body = new RespBodyBytes(HttpUtils.responseToBytes(req, INTERNAL_SERVER_ERROR, contentType, jsonRenderer)); HttpIO.INSTANCE.respond(HttpUtils.maybe(req), channel, -1, -1, 500, isKeepAlive, contentType, body, null, null); } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/RespBody.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/RespBody.java new file mode 100644 index 0000000000..0e28d47cb0 --- /dev/null +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/RespBody.java @@ -0,0 +1,32 @@ +/*- + * #%L + * rapidoid-http-fast + * %% + * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package org.rapidoid.http; + +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; + +@Authors("Nikolche Mihajlovski") +@Since("5.5.1") +public interface RespBody { + + byte[] toBytes(); + +} diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/AbstractDecoratingHttpHandler.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/AbstractDecoratingHttpHandler.java index 8f7b3b8047..5339c64608 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/AbstractDecoratingHttpHandler.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/AbstractDecoratingHttpHandler.java @@ -28,6 +28,7 @@ import org.rapidoid.http.customize.Customization; import org.rapidoid.http.impl.MaybeReq; import org.rapidoid.http.impl.ReqImpl; +import org.rapidoid.http.impl.RespImpl; import org.rapidoid.http.impl.RouteOptions; import org.rapidoid.http.impl.lowlevel.HttpIO; import org.rapidoid.jpa.JPA; @@ -139,21 +140,12 @@ private Set userRoles(Req req, String username) { } } - private TransactionMode before(final Req req, String username, Set roles) { - + private void before(final Req req, String username, Set roles) { if (U.notEmpty(options.roles()) && !Secure.hasAnyRole(username, roles, options.roles())) { throw new SecurityException("The user doesn't have the required roles!"); } req.response().view(options.view()).contentType(options.contentType()).mvc(options.mvc()); - - TransactionMode txMode = U.or(options.transaction(), TransactionMode.NONE); - - if (txMode == TransactionMode.AUTO) { - txMode = HttpUtils.isGetReq(req) ? TransactionMode.READ_ONLY : TransactionMode.READ_WRITE; - } - - return txMode; } private void execHandlerJob(final Channel channel, final boolean isKeepAlive, final MediaType contentType, @@ -180,7 +172,9 @@ public void run() { roles = userRoles(req, username); scope = auth != null ? auth.scope : null; - TransactionMode txMode = before(req, username, roles); + before(req, username, roles); + + TransactionMode txMode = TxUtil.txModeOf(req, options.transaction()); U.notNull(txMode, "txMode"); HttpWrapper[] wrappers = httpWrappers != null ? httpWrappers : U.or(Customization.of(req).wrappers(), NO_WRAPPERS); @@ -234,7 +228,7 @@ public void run() { } private Object handleReqMaybeInTx(final Channel channel, final boolean isKeepAlive, final Req req, - final Object extra, TransactionMode txMode) throws Throwable { + final Object extra, TransactionMode txMode) { if (txMode != null && txMode != TransactionMode.NONE) { @@ -250,6 +244,12 @@ public void run() { // throw to rollback Throwable err = (Throwable) res; throw U.rte("Error occurred inside the transactional web handler!", err); + + } else { + // serialize the result into a HTTP response body, while still inside tx (see #153) + RespImpl resp = (RespImpl) req.response(); // TODO find cleaner access + RespBody body = resp.resultToRespBody(res); + res = body; } result.set(res); @@ -267,21 +267,6 @@ public void run() { } } - private Runnable txWrap(final TransactionMode txMode, final Runnable handleRequest) { - if (txMode != null && txMode != TransactionMode.NONE) { - - return new Runnable() { - @Override - public void run() { - JPA.transaction(handleRequest, txMode == TransactionMode.READ_ONLY); - } - }; - - } else { - return handleRequest; - } - } - private Object wrap(final Channel channel, final boolean isKeepAlive, final Req req, final int index, final Object extra, final HttpWrapper[] wrappers, final TransactionMode txMode) throws Exception { diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/TxUtil.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/TxUtil.java new file mode 100644 index 0000000000..71d922b18b --- /dev/null +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/TxUtil.java @@ -0,0 +1,47 @@ +/*- + * #%L + * rapidoid-http-fast + * %% + * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package org.rapidoid.http.handler; + +import org.rapidoid.RapidoidThing; +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.annotation.TransactionMode; +import org.rapidoid.http.HttpUtils; +import org.rapidoid.http.Req; +import org.rapidoid.u.U; + + +@Authors("Nikolche Mihajlovski") +@Since("5.5.1") +public class TxUtil extends RapidoidThing { + + static TransactionMode txModeOf(Req req, TransactionMode configuredTxMode) { + TransactionMode txMode = U.or(configuredTxMode, TransactionMode.NONE); + + if (txMode == TransactionMode.AUTO) { + txMode = HttpUtils.isGetReq(req) ? TransactionMode.READ_ONLY : TransactionMode.READ_WRITE; + } + + return txMode; + } + + +} diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java index 61c9661164..c5df012a99 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java @@ -446,17 +446,17 @@ public synchronized Resp response() { return response; } - void doRendering(int code, byte[] responseBody) { + void doRendering(int code, RespBody body) { if (!isRendering()) { synchronized (this) { if (!isRendering()) { - respond(code, responseBody); + respond(code, body); } } } } - private void respond(int code, byte[] responseBody) { + private void respond(int code, RespBody body) { MediaType contentType = HttpUtils.getDefaultContentType(); if (tokenChanged.get()) { @@ -471,16 +471,16 @@ private void respond(int code, byte[] responseBody) { contentType = U.or(response.contentType(), contentType); } - renderResponse(code, contentType, responseBody); + renderResponse(code, contentType, body); } - private void renderResponse(int code, MediaType contentType, byte[] responseBody) { + private void renderResponse(int code, MediaType contentType, RespBody body) { rendering = true; - completed = responseBody != null; + completed = body != null; HttpIO.INSTANCE.respond( HttpUtils.maybe(this), channel, connId, handle, - code, isKeepAlive, contentType, responseBody, + code, isKeepAlive, contentType, body, response != null ? response.headers() : null, response != null ? response.cookies() : null ); @@ -544,7 +544,7 @@ private void renderResponseOrError() { String err = validateResponse(); if (err != null) { - doRendering(500, err.getBytes()); + doRendering(500, new RespBodyBytes(err.getBytes())); } else { renderResponse(); @@ -566,27 +566,27 @@ private void renderResponse() { HttpIO.INSTANCE.done(this); } else { - // first serialize the response to bytes (with error handling) - byte[] bytes = responseToBytes(); + // first create a response body from the result (with error handling) + RespBody body = toRespBody(); - // then rendering - doRendering(response.code(), bytes); + // then render the response + doRendering(response.code(), body); } } - private byte[] responseToBytes() { + private RespBody toRespBody() { try { - return response.renderToBytes(); + return response.createRespBodyFromResult(); } catch (Throwable e) { HttpIO.INSTANCE.error(this, e, LogLevel.ERROR); try { - return response.renderToBytes(); + return response.createRespBodyFromResult(); } catch (Exception e1) { Log.error("Internal rendering error!", e1); - return HttpUtils.getErrorMessageAndSetCode(response, e1).getBytes(); + return new RespBodyBytes(HttpUtils.getErrorMessageAndSetCode(response, e1).getBytes()); } } } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespBodyBytes.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespBodyBytes.java new file mode 100644 index 0000000000..1a70c2d12a --- /dev/null +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespBodyBytes.java @@ -0,0 +1,43 @@ +/*- + * #%L + * rapidoid-http-fast + * %% + * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package org.rapidoid.http.impl; + +import org.rapidoid.RapidoidThing; +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.http.RespBody; + +@Authors("Nikolche Mihajlovski") +@Since("5.5.1") +public class RespBodyBytes extends RapidoidThing implements RespBody { + + private final byte[] bytes; + + public RespBodyBytes(byte[] bytes) { + this.bytes = bytes; + } + + @Override + public byte[] toBytes() { + return bytes; + } + +} diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java index d7cadaadaf..ae0b226d7f 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java @@ -30,10 +30,7 @@ import org.rapidoid.config.Conf; import org.rapidoid.ctx.Ctxs; import org.rapidoid.ctx.UserInfo; -import org.rapidoid.http.HttpUtils; -import org.rapidoid.http.MediaType; -import org.rapidoid.http.Req; -import org.rapidoid.http.Resp; +import org.rapidoid.http.*; import org.rapidoid.http.customize.Customization; import org.rapidoid.http.customize.HttpResponseRenderer; import org.rapidoid.http.customize.LoginProvider; @@ -510,25 +507,30 @@ public String toString() { '}'; } - byte[] renderToBytes() { - if (mvc()) { + RespBody createRespBodyFromResult() { + Object result = result(); + + if (result instanceof RespBody) { + return (RespBody) result; + + } else if (mvc()) { byte[] bytes = ResponseRenderer.renderMvc(req, this); HttpUtils.postProcessResponse(this); // the response might have been changed, so post-process again - return bytes; + return new RespBodyBytes(bytes); } else if (body() != null) { - return Msc.toBytes(body()); + return new RespBodyBytes(Msc.toBytes(body())); - } else if (result() != null) { - return serializeResponseContent(); + } else if (result != null) { + return resultToRespBody(result); } else { throw U.rte("There's nothing to render!"); } } - private byte[] serializeResponseContent() { - return HttpUtils.responseToBytes(req, result(), contentType(), mediaResponseRenderer()); + public RespBody resultToRespBody(Object result) { + return new RespBodyBytes(HttpUtils.responseToBytes(req, result, contentType(), mediaResponseRenderer())); } @Override @@ -597,4 +599,5 @@ private HttpResponseRenderer mediaResponseRenderer() { return Customization.of(req).jsonResponseRenderer(); } } + } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/HttpIO.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/HttpIO.java index a925ca93e6..c023d59b68 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/HttpIO.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/HttpIO.java @@ -28,6 +28,7 @@ import org.rapidoid.http.HttpStatus; import org.rapidoid.http.MediaType; import org.rapidoid.http.Req; +import org.rapidoid.http.RespBody; import org.rapidoid.http.impl.MaybeReq; import org.rapidoid.log.LogLevel; import org.rapidoid.net.AsyncLogic; @@ -92,7 +93,7 @@ public void writeBadRequest(Channel channel) { public void respond(MaybeReq maybeReq, Channel channel, long connId, long handle, int code, boolean isKeepAlive, MediaType contentType, - byte[] body, Map headers, Map cookies) { + RespBody body, Map headers, Map cookies) { impl.respond(maybeReq, channel, connId, handle, code, isKeepAlive, contentType, body, headers, cookies); } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java index bf3ec6012c..40a29e702b 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java @@ -466,8 +466,8 @@ public boolean resumeAsync() { } else { - if (body instanceof byte[]) { - byte[] bytes = (byte[]) body; + if (body instanceof RespBody) { + byte[] bytes = ((RespBody) body).toBytes(); writeContentLengthHeader(channel, bytes.length); closeHeaders(maybeReq, output); diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/AbstractHttpProcessor.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/AbstractHttpProcessor.java index 7ace647d01..4f60ca7869 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/AbstractHttpProcessor.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/AbstractHttpProcessor.java @@ -7,9 +7,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/NotFoundHttpProcessor.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/NotFoundHttpProcessor.java index 40c28d5bfd..3113dc574e 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/NotFoundHttpProcessor.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/processor/NotFoundHttpProcessor.java @@ -25,6 +25,8 @@ import org.rapidoid.annotation.Since; import org.rapidoid.http.HttpUtils; import org.rapidoid.http.MediaType; +import org.rapidoid.http.RespBody; +import org.rapidoid.http.impl.RespBodyBytes; import org.rapidoid.http.impl.lowlevel.HttpIO; import org.rapidoid.net.abstracts.Channel; import org.rapidoid.net.impl.RapidoidHelper; @@ -41,7 +43,9 @@ public NotFoundHttpProcessor() { public void onRequest(Channel channel, RapidoidHelper data) { boolean isKeepAlive = data.isKeepAlive.value; - HttpIO.INSTANCE.respond(HttpUtils.noReq(), channel, -1, -1, 404, isKeepAlive, MediaType.PLAIN_TEXT_UTF_8, "Not found!".getBytes(), null, null); + RespBody body = new RespBodyBytes("Not found!".getBytes()); + + HttpIO.INSTANCE.respond(HttpUtils.noReq(), channel, -1, -1, 404, isKeepAlive, MediaType.PLAIN_TEXT_UTF_8, body, null, null); channel.send().closeIf(!isKeepAlive); } diff --git a/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/JPALazyEntitiesTest.java b/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/JPALazyEntitiesTest.java new file mode 100644 index 0000000000..b1abd21183 --- /dev/null +++ b/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/JPALazyEntitiesTest.java @@ -0,0 +1,65 @@ +/*- + * #%L + * rapidoid-integration-tests + * %% + * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package org.rapidoid.jpa; + +import org.junit.Test; +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.http.IsolatedIntegrationTest; +import org.rapidoid.setup.On; + +@Authors("Nikolche Mihajlovski") +@Since("5.5.1") +public class JPALazyEntitiesTest extends IsolatedIntegrationTest { + + @Test + public void testLazyEntitiesWithoutTx() { + JPA.bootstrap(path()); + initData(); + + On.get("/movies").json(() -> JPA.of(Movie.class).all()); + + onlyGet("/movies"); + } + + @Test + public void testLazyEntitiesInTx() { + JPA.bootstrap(path()); + initData(); + + On.get("/movies").transaction().json(() -> JPA.of(Movie.class).all()); + + onlyGet("/movies"); + } + + private void initData() { + JPA.transaction(() -> { + Movie movie = new Movie("Mr. Bean"); + + Tag tag = new Tag("comedy"); + JPA.save(tag); + + movie.tags.add(tag); + JPA.insert(movie); + }); + } + +} diff --git a/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Movie.java b/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Movie.java index a99ded1234..b81e2e41a7 100644 --- a/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Movie.java +++ b/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Movie.java @@ -21,9 +21,9 @@ package org.rapidoid.jpa; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; @Entity public class Movie extends AbstractEntity { @@ -34,6 +34,9 @@ public class Movie extends AbstractEntity { private String title; + @OneToMany(fetch = FetchType.LAZY) + public Set tags = new HashSet<>(); + public Movie() { } diff --git a/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Tag.java b/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Tag.java new file mode 100644 index 0000000000..77272bab55 --- /dev/null +++ b/rapidoid-integration-tests/src/test/java/org/rapidoid/jpa/Tag.java @@ -0,0 +1,59 @@ +/*- + * #%L + * rapidoid-integration-tests + * %% + * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package org.rapidoid.jpa; + + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Tag { + + @Id + @GeneratedValue + private Long id; + + private String name; + + public Tag() { + } + + public Tag(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesInTx/GET_movies b/rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesInTx/GET_movies new file mode 100644 index 0000000000..9acbacfbc6 --- /dev/null +++ b/rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesInTx/GET_movies @@ -0,0 +1,8 @@ +HTTP/1.1 200 OK +Connection: keep-alive +Server: Rapidoid +Date: XXXXX GMT +Content-Type: application/json +Content-Length: 63 + +[{"id":1,"title":"Mr. Bean","tags":[{"id":1,"name":"comedy"}]}] \ No newline at end of file diff --git a/rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesWithoutTx/GET_movies b/rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesWithoutTx/GET_movies new file mode 100644 index 0000000000..9acbacfbc6 --- /dev/null +++ b/rapidoid-integration-tests/src/test/resources/test-results/JPALazyEntitiesTest/testLazyEntitiesWithoutTx/GET_movies @@ -0,0 +1,8 @@ +HTTP/1.1 200 OK +Connection: keep-alive +Server: Rapidoid +Date: XXXXX GMT +Content-Type: application/json +Content-Length: 63 + +[{"id":1,"title":"Mr. Bean","tags":[{"id":1,"name":"comedy"}]}] \ No newline at end of file