how to write new entity in postCalculateUnitOfWorkChangeSet or preCommitTransaction - eclipselink

I am using Eclipselink and want to write changlog (an entity) for all insert/change/delete business enitities. I found the hooks postCalculateUnitOfWorkChangeSet or preCommitTransaction of SessionEventAdapter where I can build and insert changelog basing on the changeset.
How can I get the changeset for insert/update/delete object in the two methods?
How can I insert new changelog entities with the same unit of work because I need they happen in the same transaction.
What I tried is as below:
#Override
public void postCalculateUnitOfWorkChangeSet(SessionEvent event) {
Object source = event.getSource();
if (source instanceof UnitOfWork) {
UnitOfWork unitOfWork = (UnitOfWork) source;
UnitOfWorkChangeSet unitOfWorkChangeSet = unitOfWork.getUnitOfWorkChangeSet();
if (unitOfWorkChangeSet != null && unitOfWorkChangeSet.hasChanges()) {
Map allChangeSets = unitOfWorkChangeSet.getAllChangeSets();
allChangeSets.forEach((k, v) -> {
if (v instanceof ObjectChangeSet) {
ObjectChangeSet ocs = (ObjectChangeSet) v;
if (ocs.isNew()) {
Object unitOfWorkClone = ocs.getUnitOfWorkClone();
if (unitOfWorkClone instanceof Traceable) {
ChangeLog changeLog = getChangeLog(Operation.INSERT, (Traceable) unitOfWorkClone);
unitOfWork.registerNewObject(changeLog);
unitOfWork.getParent().insertObject(changeLog);
}
} else {
List<ChangeRecord> changes = ((ObjectChangeSet) v).getChanges();
changes.forEach(c -> {
Object unitOfWorkClone = ((ObjectChangeSet) (c.getOwner())).getUnitOfWorkClone();
if (unitOfWorkClone instanceof Traceable) {
ChangeLog changeLog = getChangeLog(Operation.UPDATE, (Traceable) unitOfWorkClone);
unitOfWork.getParent().insertObject(changeLog);
}
});
}
}
}
);
}
}
}
This can work but :
1. I am not sure if it is right way to get the changeset because I did not find document on this.
2. It cannot batch write. When I batch insert business objects, the business obejcts can be written in batch. But the changelogs are inserted one by one.
I also tried history policy. but it cannot work well with batch write.
eclipselink batch write is disabled when use history policy or DescriptorEventAdapter
Thanks a lot for your any comments.

Related

ASP.NET Core 7 WebApplicationFactory Integration tests. How to load data?

I am creating an integration test to check that the data is working based on this very good tutorial.
The tutorial loads sample data in the OnModelCreating. But I was unsure if doing that will repeatedly load data to the DB when running the program.
However although I can get the index page to load, it has the page content, such as the table structure for the data it doesn't have the data from the database.
Using Swagger I copied a sample of data as JSON, saved it to a file, capitalized the first letter of the key to make it the same as the properties (after not doing do was fruitless as well), and tried to add it to the context.
internal static class AddTestData
{
//import json array and add to context
public static void AddMovieData(ApplicationDbContext context)
{
var jsonString = File.ReadAllText("testMoviedata.json");
var list = JsonSerializer.Deserialize<List<Movie>>(jsonString);
{
foreach (var item in list)
{
context.Movie.Add(item);
}
context.SaveChanges();
}
}
}
and tried to add it to the dbcontext in this process in the WebApplicationFactory Class from HERE
public class TestingWebAppFactory<TEntryPoint> : WebApplicationFactory<Program> where TEntryPoint : Program
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
......... stuff deleted for brevity...
using (var appContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
try
{
appContext.Database.EnsureCreated();
// Seed the database with test data.
AddTestData.AddMovieData(appContext);
}
catch (Exception ex)
{
//Log errors or do anything you think it's needed
throw;
}
}
... still nothin. Page loads, no data loads.
Also why can't I get breakpoints to work in the Integration project?
What am I doing wrong?
Solved!!!
The code was OK,but the data wasn't being deserialised.
I had to move it to the main project and test it there.
The solution is
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var list = JsonSerializer.Deserialize<Movie[]>(jsonString, options);

Wait for multiple statements to execute with R2DBC

I need to execute multiple statement with R2DBC but couldn't find useful DatabaseClient#inConnection* examples... So my function keeps getting out too early:
public Publisher<Person> groupStatements(DatabaseClient client, Person person) {
// yes, I know that's harsh, but hey! so is JPA's #ElementCollection
return client.sql("DELETE FROM persons_address WHERE person = :id")
.bind("id", person.getId())
.fetch().rowsUpdated()
.map(deleted -> {
// now recreate every relationship
GenericExecuteSpec statement = client.sql("INSERT INTO persons_address (person, address) VALUES (:person, :address)");
person.getOfficePlaces().forEach(address -> {
statement
.bind("person", person.getId()).bind("address", address.getId())
.fetch().rowsUpdated() // there we go AWOL
.subscribe(inserted -> {
// logging here
});
});
return person; //FIXME wait! need above grouped statements to complete
});
}
NB: I'm using H2 as a backend.
Thanks for any information!
I found a proper batch processing technic (here replacing the map/deleted section), but even got stuck because Statement#execute is returning a Publisher with only #subscribe method and I couldn't return from the chain. So I fed the beast with a few gearing
//DEBUG I couldn't figure out how to use labels! good enough
private static final String SQL_INSERT = "INSERT INTO persons_address (person, address) VALUES ($1, $2)";
...
.flatMap(deleted -> {
if (person.getOfficePlaces().isEmpty()) {
return Mono.just(person);
} else {
return client.inConnection(cnx -> {
Statement stmt = cnx.createStatement(SQL_INSERT);
person.getOfficePlaces().forEach(address -> {
stmt.bind(0, person.getId()).bind(1, address.getId()).add();
});
return Flux.from(stmt.execute()).last().map(dontcare -> person);
});
}

RavenDB querying metadata

I want to prevent documents from being deleted in my project and I decided to use metadata to mark document as Archived. I used below code to do that:
public class DeleteDocumentListener : IDocumentDeleteListener
{
public void BeforeDelete(string key, object entityInstance, RavenJObject metadata)
{
metadata.Add("Archived", true);
throw new NotSupportedException();
}
}
After that I wanted to alter query to return only documents which have Archived metadata value set to false:
using (var session = _store.OpenSession())
{
var query = session.Advanced.DocumentQuery<Cutter>()
.WhereEquals("#metadata.Archived", false);
}
Unfortunately this query return empty result set. It occurs that if Document doesn't have this metadata property then above condition is treated as false. It wasn't what I expected.
How can I compose query to return Documents which don't have metadata property or this property has some value ?
You can solve it by creating an index for you Cutter documents and then query against that:
public class ArchivedIndex : AbstractIndexCreationTask<Cutter>
{
public class QueryModel
{
public bool Archived { get; set; }
}
public ArchivedIndex()
{
Map = documents => from doc in documents
select new QueryModel
{
Archived = MetadataFor(doc)["Archived"] != null && MetadataFor(doc).Value<bool>("Archived")
};
}
}
Then query it like this:
using (var session = documentStore.OpenSession())
{
var cutters = session.Query<ArchivedIndex.QueryModel, ArchivedIndex>()
.Where(x => x.Archived == false)
.OfType<Cutter>()
.ToList();
}
Hope this helps!
Quick side note. To create the index, the following code may need to be run:
new ArchivedIndex().Execute(session.Advanced.DocumentStore);

What's the fastest way to find Tags pointing to Commits?

With libgit2sharp I would like to do the following:
foreach( Commit commit in repo.Commits )
{
// How to implement assignedTags?
foreach( Tag tag in commit.assignedTags ) {}
}
I want to get all tags assigned to the current commit. Whats the best way to do that? Iterate through all Tags and see if tag.Target.Sha == commit.Sha? Thats not very performant. Is there another way?
So I want to get all tags assigned to the current commit. Whats the best way to do that? Iterate through all Tags and see if tag.Target.Sha == commit.Sha? Thats not very performant. Is there another way?
There are two things to take into account when it comes to Tags.
A Tag can point to something else than a Commit (A Tree or a Blob, for instance)
A Tag can point to another Tag (chained annotated tags)
The code below should fit your need by taking these points above into account.
Note: repo.Commits will only enumerate the commits reachable from the current branch (HEAD). The code below
extends this to easily browse all the reachable commits.
...
using (var repo = new Repository("Path/to/your/repo"))
{
// Build up a cached dictionary of all the tags that point to a commit
var dic = TagsPerPeeledCommitId(repo);
// Let's enumerate all the reachable commits (similarly to `git log --all`)
foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter {Since = repo.Refs}))
{
foreach (var tags in AssignedTags(commit, dic))
{
Console.WriteLine("Tag {0} points at {1}", tags.Name, commit.Id);
}
}
}
....
private static IEnumerable<Tag> AssignedTags(Commit commit, Dictionary<ObjectId, List<Tag>> tags)
{
if (!tags.ContainsKey(commit.Id))
{
return Enumerable.Empty<Tag>();
}
return tags[commit.Id];
}
private static Dictionary<ObjectId, List<Tag>> TagsPerPeeledCommitId(Repository repo)
{
var tagsPerPeeledCommitId = new Dictionary<ObjectId, List<Tag>>();
foreach (Tag tag in repo.Tags)
{
GitObject peeledTarget = tag.PeeledTarget;
if (!(peeledTarget is Commit))
{
// We're not interested by Tags pointing at Blobs or Trees
continue;
}
ObjectId commitId = peeledTarget.Id;
if (!tagsPerPeeledCommitId.ContainsKey(commitId))
{
// A Commit may be pointed at by more than one Tag
tagsPerPeeledCommitId.Add(commitId, new List<Tag>());
}
tagsPerPeeledCommitId[commitId].Add(tag);
}
return tagsPerPeeledCommitId;
}
Here is another version of nulltoken's answer but with using ILookupclass instead of dictionary. A bit nicer IMO:
private static ILookup<ObjectId, Tag> CreateCommitIdToTagLookup(Repository repo)
{
var commitIdToTagLookup =
repo.Tags
.Select(tag => new { Commit = tag.PeeledTarget as Commit, Tag = tag })
.Where(x => x.Commit != null)
.ToLookup(x => x.Commit.Id, x => x.Tag);
return commitIdToTagLookup;
}
and simple usage example:
using (var repo = new Repository("Path/to/your/repo"))
{
var commitIdToTagLookup = CreateCommitIdToTagLookup(repo);
foreach (var commit in repo.Commits)
{
foreach (var tag in commitIdToTagLookup[commit.Id])
{
Console.WriteLine($"Tag {tag.FriendlyName} points at {commit.Id}");
}
}
}

Generate test data in Raven DB

I am looking for a preferred and maintainable way of test data generation in Raven DB. Currently, our team does have a way to do it through .NET code. Example is provided.
However, i am looking for different options. Please share.
public void Execute()
{
using (var documentStore = new DocumentStore { ConnectionStringName = "RavenDb" })
{
documentStore.Conventions.DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites;
// Override the default key prefix generation strategy of Pascal case to lower case.
documentStore.Conventions.FindTypeTagName = type => DocumentConvention.DefaultTypeTagName(type).ToLower();
documentStore.Initialize();
InitializeData(documentStore);
}
}
Edit: Raven-overflow is really helpful. Thanks for pointing out to the right place.
Try checking out RavenOverflow. In there, I've got a FakeData project that has fake data (both hardcoded AND randomly generated). This can then be used in either my Tests project or the Main Website :)
Here's some sample code...
if (isDataToBeSeeded)
{
HelperUtilities.CreateSeedData(documentStore);
}
....
public static void CreateSeedData(IDocumentStore documentStore)
{
Condition.Requires(documentStore).IsNotNull();
using (IDocumentSession documentSession = documentStore.OpenSession())
{
// First, check to make sure we don't have any data.
var user = documentSession.Load<User>(1);
if (user != null)
{
// ooOooo! we have a user, so it's assumed we actually have some seeded data.
return;
}
// We have no users, so it's assumed we therefore have no data at all.
// So let's fake some up :)
// Users.
ICollection<User> users = FakeUsers.CreateFakeUsers(50);
StoreFakeEntities(users, documentSession);
// Questions.
ICollection<Question> questions = FakeQuestions.CreateFakeQuestions(users.Select(x => x.Id).ToList());
StoreFakeEntities(questions, documentSession);
documentSession.SaveChanges();
// Make sure all our indexes are not stale.
documentStore.WaitForStaleIndexesToComplete();
}
}
....
public static ICollection<Question> CreateFakeQuestions(IList<string> userIds, int numberOfFakeQuestions)
{
.... you get the idea .....
}