Spring MongoTemplate not a part of ongoing transaction - spring-data-mongodb

I am attempting to transition to using MongoDB Transactions via Spring Data Mongo now that MongoDB 4.0 supports transactions, and Spring Data Mongo 2.1.5.Release supports it as well.
According to the Spring Data Mongo Documentation, you should be able to use the Spring MongoTransactionManager and have the MongoTemplate recognize and participate in ongoing transactions: https://docs.spring.io/spring-data/mongodb/docs/2.1.5.RELEASE/reference/html/#_transactions_with_mongotransactionmanager
However, this following test fails:
#Autowired
private TestEntityRepository testEntityRepository;
#Autowired
private MongoTemplate mongoTemplate;
#BeforeTransaction
public void beforeTranscation() {
cleanAndInitDatabase();
}
#Test
#Transactional
public void transactionViaAnnotation() {
TestEntityA entity1 = new TestEntityA();
entity1.setValueA("a");
TestEntityA entity2 = new TestEntityA();
entity2.setValueA("b");
testEntityRepository.save(entity1);
testEntityRepository.save(entity2);
// throw new RuntimeException("prevent commit");
List<TestEntityA> entities = testEntityRepository.findAll(Example.of(entity1));
Assertions.assertEquals(1, entities.size()); // SUCCEEDS
entities = testEntityRepository.findAll(Example.of(entity2));
Assertions.assertEquals(1, entities.size()); // SUCCEEDS
entities = mongoTemplate.findAll(TestEntityA.class);
Assertions.assertEquals(2, entities.size()); // FAILS - expected: <2> but was: <0>
}
It appears that the testEntityRepository works fine with the transaction. The asserts succeed, and if I uncomment the exception line, neither of the records are persisted to the database.
However, trying to use the mongoTemplate directly to do a query doesn't work as it appears to not participate in the transaction.
The documentation I have linked shows using the template directly within a #Transactional method like I am attempting. However, the text says
MongoTemplate can also participate in other, ongoing transactions.
which could be interpreted to mean the template can be used with different transactions, and not necessarily the implicit transaction. But that is not what the example would indicate.
Any ideas what is happening or how to get the template to participate in the same implicit transaction?

Related

How to profile Entity Framework activity against SQL Server?

It's easy to use SQL Server Profiler to trace stored procedures activity. But how to trace SQL queries issued by LINQ via Entity Framework? I need to identify such queries (LINQ code) that consume a lot of time, are called most frequently and therefore are the first candidates for optimization.
Add this key to your connection string:
Application Name=EntityFramework
And filter by this in Profiler
Adding #ErikEJ's answer : if you are using .net Core, so you are using EFCore. There are no Database.Log property. You should use OnConfiguring override of your DbContext class and then
optionsBuilder.LogTo(Console.WriteLine);
Sample :
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.LogTo(Console.WriteLine);
}
}
I've found useful DbContext.Database.Log property.
MSDN article Logging and Intercepting Database Operations
The DbContext.Database.Log property can be set to a delegate for any method that takes a string. Most commonly it is used with any TextWriter by setting it to the “Write” method of that TextWriter. All SQL generated by the current context will be logged to that writer. For example, the following code will log SQL to the console:
using (var context = new BlogContext())
{
context.Database.Log = Console.Write;
// Your code here...
}
What gets logged?
When the Log property is set all of the following will be logged:
The approximate amount of time it took to execute the command. Note that this is the time from sending the command to getting the result object back. It does not include time to read the results.
SQL for all different kinds of commands. For example:
Queries, including normal LINQ queries, eSQL queries, and raw queries from methods such as SqlQuery
Inserts, updates, and deletes generated as part of SaveChanges
Relationship loading queries such as those generated by lazy loading
Parameters
Whether or not the command is being executed asynchronously
A timestamp indicating when the command started executing
Whether or not the command completed successfully, failed by throwing an exception, or, for async, was canceled
Some indication of the result value

Where to implement Entity Framework database transactions in modern web applications?

Let’s assume that the primary components in your application are an Angular client, which calls an ASP.NET Web API, which uses Entity Framework to perform CRUD operations on your database. So, for example, in your API controllers, the Post (Add) method adds a new entity to the database context and then commits it to the database by calling the Entity Framework SaveChanges method.
This works fine when only one record needs to be added to the database at a time.
But, what if, for example, you want to add several records of different entity types to your database in one transaction? Where do you implement the Database.BeginTransaction and Database.CommitTransaction/RollbackTransaction? If you add a service layer to accomplish this, then what does the Angular client call?
PLEASE SEE BELOW FOR FURTHER DETAIL AND QUESTIONS.
I want to provide more detail about my current approach to solving this problem and ask the following questions:
(1) Is this a good approach, or is there a better way?
(2) My approach does not port to .NET Core, since .NET Core does not support OData yet (see https://github.com/OData/WebApi/issues/229). Any thoughts or ideas about this?
I have stated the problems that I faced and the solutions that I chose below. I will use a simple scenario where a customer is placing an order for several items – so, there is one Order record with several OrderDetail records. The Order record and associated OrderDetail records must be committed to the database in a single transaction.
Problem #1: What is the best way to send the Order and OrderDetail records from the Angular client to the ASP.NET Web API?
Solution #1: I decided to use OData batching, so that I could send all the records in one POST. I am using the datajs library to perform the batching (https://www.nuget.org/packages/datajs).
Problem #2: How do I wrap a single transaction around the Order and OrderDetail records?
Solution #2: I set up an OData batch endpoint in my Web API, which involved the following:
(1) In the client, configure a batch request route.
// Configure the batch request route.
config.Routes.MapODataServiceRoute(
routeName: "batch",
routePrefix: "batch",
model: builder.GetEdmModel(),
pathHandler: new DefaultODataPathHandler(),
routingConventions: conventions,
batchHandler: new TransactionalBatchHandler(GlobalConfiguration.DefaultServer));
}
(2) In the Web API, implement a custom batch handler, which wraps a database transaction around the given OData batch. The batch handler starts the transaction, calls the appropriate ODataController to perform the CRUD operation, and then commits/rolls back the transaction, depending on the results.
/// <summary>
/// Custom batch handler specialized to execute batch changeset in OData $batch requests with transactions.
/// The requests will be executed in the order they arrive, that means that the client is responsible for
/// correctly ordering the operations to satisfy referential constraints.
/// </summary>
public class TransactionalBatchHandler : DefaultODataBatchHandler
{
public TransactionalBatchHandler(HttpServer httpServer)
: base(httpServer)
{
}
/// <summary>
/// Executes the batch request and wraps the execution of the whole changeset within a transaction.
/// </summary>
/// <param name="requests">The <see cref="ODataBatchRequestItem"/> instances of this batch request.</param>
/// <param name="cancellation">The <see cref="CancellationToken"/> associated with the request.</param>
/// <returns>The list of responses associated with the batch request.</returns>
public async override Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(
IEnumerable<ODataBatchRequestItem> requests,
CancellationToken cancellation)
{
if (requests == null)
{
throw new ArgumentNullException("requests");
}
IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
try
{
foreach (ODataBatchRequestItem request in requests)
{
OperationRequestItem operation = request as OperationRequestItem;
if (operation != null)
{
responses.Add(await request.SendRequestAsync(Invoker, cancellation));
}
else
{
await ExecuteChangeSet((ChangeSetRequestItem)request, responses, cancellation);
}
}
}
catch
{
foreach (ODataBatchResponseItem response in responses)
{
if (response != null)
{
response.Dispose();
}
}
throw;
}
return responses;
}
private async Task ExecuteChangeSet(
ChangeSetRequestItem changeSet,
IList<ODataBatchResponseItem> responses,
CancellationToken cancellation)
{
ChangeSetResponseItem changeSetResponse;
// Since IUnitOfWorkAsync is a singleton (Unity PerRequestLifetimeManager) used by all our ODataControllers,
// we simply need to get a reference to it and use it for managing transactions. The ODataControllers
// will perform IUnitOfWorkAsync.SaveChanges(), but the changes won't get committed to the DB until the
// IUnitOfWorkAsync.Commit() is performed (in the code directly below).
var unitOfWorkAsync = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnitOfWorkAsync)) as IUnitOfWorkAsync;
unitOfWorkAsync.BeginTransaction();
// This sends each request in the changeSet to the appropriate ODataController.
changeSetResponse = (ChangeSetResponseItem)await changeSet.SendRequestAsync(Invoker, cancellation);
responses.Add(changeSetResponse);
if (changeSetResponse.Responses.All(r => r.IsSuccessStatusCode))
{
unitOfWorkAsync.Commit();
}
else
{
unitOfWorkAsync.Rollback();
}
}
}
You do not need to implement Database.BeginTransaction and Database.CommitTransaction/RollbackTransaction if you are using Entity Framework. Entity Framework implements UnitOfWork. The only thing that you should care about is to work with a different instance of DbContext for every web request, but exaclty 1 instance for 1 request and call SaveChanges only 1 time when you made all the changes you need.
In case of any Exception during SaveChanges all the changes will be rolled back.
The angular client should not care about this, it only sends the data and checks if everything was fine.
This is very easy to do if you use an IoC framework, like Unity and let your DbContext injected in your Controller or Service.
In this case you should use the following settings (if you use Unity):
container.RegisterType<DbContext, YourDbContext>(new PerRequestLifetimeManager(), ...);
Then you can do this if you want to use it in a Controller:
public class YourController : Controller
{
private YourDbContext _db;
public YourController(DbContext context)
{
_db = context;
}
...
No need to over-complicate things. Add the code to the WebApi project. Pass around your Transaction object and re-use it. See https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx for an example.

Best Approach for JPA lazy loading over Rest/AngularJS

Sorry for the general question, but is there an approach for still using JPA lazy loading of entities, when developing a restful AngularJS application.
In the old JSF days, it would all just work when a backing bean accessed the list.
I am using EclipseLink and Spring Data, with Jersey for the restful end points.
Regards
I
Generally you'd have to trigger the lazy loading of the entities prior the EntityManager being closed during the lifecycle of the request.
To do so, you can use the "Open EntityManager in View" pattern. Spring provides a Filter you can apply: OpenEntityManagerInViewFilter (read the docs here: http://docs.spring.io/spring/docs/4.1.0.RELEASE/javadoc-api/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.html).
Alternatively, you can manually call getMyLazyCollection() on your JPA entity(ies) prior to serializing them to JSON.
I think the best course depends on following.
Are you able to retrieve the fully resolved entity i.e. all of its
components without adversely affecting performance ?
If the answer is Yes then go for resolving the full entity using JPA fetch option=eager.
I the answer is No. I would go for the following approach:-
1) Expose every lazy JPA component/association explicitly as a sub-resource.
e.g.
#Entity
public class Employee {
#Id
private long id;
...
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ADDR_ID")
private Address homeAddress;
...
}
#Entity
public class Address{
}
2) Expose service as controller(although you can have them separated but I don't recommend)
#RequestMapping(value="/api/employee")
#Controller
public class EmployeeSvc
public Employee getEmployee(#PathVariable empID){
.....
}
#RequestMapping(value="{empID}/homeaddress")
public Address getHomeAddress(#PathVariable empID){
// will serve http://localhost:8080/api/employee/11111/homeaddress
return this.getEmployee(empID).getHomeAddress();
//make sure you are using 2nd Level cache - as you retrieve object twice
}
}

Initialize Spring embedded database after deployment

I have an Spring MVC app with an embedded database (HSQLDB) that I want to initialize after deployment. I know that I could use an xml script to define initial data for my datasource but, as long I'm using JPA + Hibernate, I would like to use Java code. Is there a way to do this?
Heavily updated answer (it was too complex before):
All you need is to add initializing bean to your context, which will insert all the necessary data into the database:
public class MockDataPopulator {
private static boolean populated = false;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
public void populateDatabase() {
// Prevent duplicate initialization as HSQL is also initialized only once. Duplicate executions
// can happen when the application context is reloaded - e.g. when running unit tests).
if (populated) {
return;
}
// Create new persistence session
Session session = sessionFactory.openSession();
session.setFlushMode(FlushMode.ALWAYS);
// Insert mock entities
session.merge(MockDataFactory.createMyFirstObject())
session.merge(MockDataFactory.createMySeconfObject())
// ...
// Flush and close
session.flush();
session.close();
// Set initialization flag
populated = true;
}
}

How ria services manage transactions

I learnt how can we configure transactions in Entity Framework using TransactionScope in one other question of mine. However it still confuses me! I mean how does RIA services execute transactions and how can we specify transaction options? I mean, suppose on the client in Silverlight we specify something like this :-
someContext.Add(someEntity1);
someContext.Add(someEntity2);
someContext.Add(someEntity3);
Now when i call someContext.SubmitChanges() this is going to call InsertSomeEntity() on the server in my domain service class. What is the guarantee that all three records will be inserted into the database and if one fails all of them fails? And how can we change these options?
Chand's link has a good example. WCF RIA will submit a ChangeSet for the SubmitChanges containing all 3 Add's. In your DomainService, you can override the PersistChanges method to complete the transaction.
public class SomeEntityDomainService : DomainService
{
SomeEFContext _someEFContext;
public SomeEntityDomainService()
{
_someEFContext = new SomeEFContext();
}
public void InsertSomeEntity(SomeEntity someEntity)
{
// Called 3 times in your example
_someEFContext.SomeEntities.Add(someEntity);
}
protected override bool PersistChangeSet()
{
// Called exactly once per SubmitChanges() in Silverlight
_someEFContext.SaveChanges();
}
}
All of this happens in one request from the client to the server, not 3 requests.

Resources