Entity, Entity Groups and List using Objectify 4 - google-app-engine

I'm having some difficulty to solve this kind of problem:
I Have some nested entities and I'm trying to persist them in the same transaction (HRD enable).
Entity A:
#Entity
public class A {
#Id Long id;
List<B> children;
}
Entity B:
#Entity
public class B {
#Id Long id;
}
When I try to persist 6 instances (just two Entity Groups, A e B) ...
public void testOfy() {
ofy.getFactory().register(A.class);
ofy.getFactory().register(B.class);
List<B> list = new ArrayList<B>();
final A a0 = new A();
final B b1 = new B();
final B b2 = new B();
final B b3 = new B();
final B b4 = new B();
final B b5 = new B();
Ofy o = ofy.transaction();
try {
o.save().entities(b1).now(); list.add(b1);
o.save().entities(b2).now(); list.add(b2);
o.save().entities(b3).now(); list.add(b3);
o.save().entities(b4).now(); list.add(b4);
o.save().entities(b5).now(); list.add(b5);
a0.children = list;
o.save(a0);
o.getTxn().commit();
}
finally {
if (o.getTxn().isActive())
o.getTxn().rollback();
}
}
I get the Exception:
java.lang.IllegalArgumentException: operating on too many entity groups in a single transaction.
at com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:36)
However if I put just 5 instances everything works...
public void testOfy() {
ofy.getFactory().register(A.class);
ofy.getFactory().register(B.class);
List<B> list = new ArrayList<B>();
final A a0 = new A();
final B b1 = new B();
final B b2 = new B();
final B b3 = new B();
final B b4 = new B();
final B b5 = new B();
Ofy o = ofy.transaction();
try {
o.save().entities(b1).now(); list.add(b1);
o.save().entities(b2).now(); list.add(b2);
o.save().entities(b3).now(); list.add(b3);
o.save().entities(b4).now(); list.add(b4);
// o.save().entities(b5).now(); list.add(b5);
a0.children = list;
o.save(a0);
o.getTxn().commit();
}
finally {
if (o.getTxn().isActive())
o.getTxn().rollback();
}
}
I'm using Objectify 4.0b3, does anyone have any suggestion?
Thank you!

You have misunderstood what an entity group is. An entity group is not a class, it is an instance (or a group of instances).
Each of those entities represent a separate entity group. XG transactions allow a maximum of five EGs per transaction. The 6th produces the error you see.

Related

Guys I don't understand how to write tests

I have a class in which I get the number of ids per year with a soql query on a custom object:
public static Integer numberRecords(Integer year) {
List<AggregateResult> numbersOfRecords = [
SELECT Keeper__c
FROM Month_Expense_Application__c
WHERE calendar_year(MonthDate__c) =: year
];
Set<Id> keepers = new Set<Id>();
for (AggregateResult result : numbersOfRecords) {
keepers.add((Id)result.get('Keeper__c'));
}
Integer value = keepers.size();
return value;
}
I'm trying to write a test for this method by creating an object and filling in the fields it needs, then I try to get the id with a soql request and compare:
#isTest
public class ExpenseAdminControllerTests {
#isTest
public static void numberRecordsTests() {
Month_Expense_Application__c monthExpenseTest = new Month_Expense_Application__c(
Balance__c = 12.3,
Income__c = 11,
Keeper__c = '0034x00001K7kGCAAZ',
MonthDate__c = date.newInstance(2022, 11, 21)
);
Integer year = 2022;
List<Month_Expense_Application__c> numbersOfRecords = [
SELECT Keeper__c
FROM Month_Expense_Application__c
WHERE calendar_year(MonthDate__c) =: year AND Id =: monthExpenseTest.Id
];
Set<Id> keepers = new Set<Id>();
keepers.add(numbersOfRecords[0].Keeper__c);
Integer value = keepers.size();
System.assertEquals(1, value);
}
}
But i cant tell me what am i doing wrong
I guess you just forgot to insert "monthExpenseTest" in actual database, by means that you have just created a object by using below lines as you mentioned
Month_Expense_Application__c monthExpenseTest = new Month_Expense_Application__c(
Balance__c = 12.3,
Income__c = 11,
Keeper__c = '0034x00001K7kGCAAZ',
MonthDate__c = date.newInstance(2022, 11, 21)
);
by writing above lines you just created object which is present in memory only. it is not stored in database, so that it is not fetched when you try to fetch it via SOQL Query.
Now you may want to add one more line after above code.which is given as below
insert monthExpenseTest ;
so after inserting your object is stored in database. now you can fetch it via SOQL query.
So your test class will be looks like below
#isTest
public class ExpenseAdminControllerTests {
#isTest
public static void numberRecordsTests() {
Month_Expense_Application__c monthExpenseTest = new Month_Expense_Application__c(
Balance__c = 12.3,
Income__c = 11,
Keeper__c = '0034x00001K7kGCAAZ',
MonthDate__c = date.newInstance(2022, 11, 21)
);
insert monthExpenseTest;
Integer year = 2022;
Integer keepers = ExpenseAdminController.numberRecords(year);
System.assertEquals(1, keepers);
}
}
So when you are testing ExpenseAdminController then you just need to call that method because you want to test it. in test class which is mentioned in question it not testing numberRecords() method instead of you are rewriting logic.

Dapper ORM not accepting multiple models

I am having a problem were Dapper won't accept multiple models, see my code example. I have the QueueItems and robots models in query but when I add the second (Robots) I get the folowing error "IDBconnection does not contain a definition for Query"
public void getdata()
{
using (IDbConnection connection = new SqlConnection(con))
{
string sql = "Select * from QueueItems";
var queryResult = connection.Query<QueueItems,Robots>(sql).ToList();
}
}
If your resultset contains columns for two pocos, you'll have to wrap them in an envelope (either a typed class or dynamic) object since you can only return one type.
The method signature for that is
var result = connection.Query<QueueItems,Robots,dynamic>(sql, (queueItem,robot) => {
dynamic d = new ExpandoObject();
d.QueueItem = queueItem
d.Robot = robot;
return d;
}, splitOn: "...insert split columns unless they're named Id...");
This will return a dynamic list where each record contains a QueueItem and Robot property.
Dapper by default splits on the "Id" column. If your query returns a recordset like this:
|Id|C1|C2|C3|Id|C4|C5
Id, C1, C2 and C3 will be mapped to queueItem, Id, C4 and C5 will be mapped to robot.

Static class fields in Chapel

Does Chapel have the equivalent of the C++/Java static class field whose value is shared between class objects? If not, what should one do?
Chapel does not support an equivalent of static class fields. However, it does support an equivalent of static class methods, referred to as type methods in Chapel.
If one needed static class fields in Chapel, they could use a parentheses-less method returning a globally defined variable for a similar effect, e.g.
var globalValue = 42;
class C {
var a = 1;
proc b {
return globalValue;
}
}
var c1 = new owned C(1);
var c2 = new owned C(2);
writeln(c1.a);
writeln(c1.b);
writeln(c2.a);
writeln(c2.b);
globalValue += 1;
writeln(c1.a);
writeln(c1.b);
writeln(c2.a);
writeln(c2.b);
Output:
1
42
2
42
1
43
2
43

QueryDSL isNotEmpty expression failing

I'm trying to execute an expression that returns a set of results where the collection attribute is not empty. Here are my classes
A.class
#Document
public class A {
#Id String id;
String name;
List<B> matches;
}
B.class
#Document
public class B {
String name;
}
My test case
#Test
public void testFindWhereCollectionNotEmpty() {
B b1 = new B();
b1.name = "b1";
B b2 = new B();
b2.name = "b2";
template.save(b1);
template.save(b2);
A a1 = new A();
a1.id = "p1";
a1.matches = Arrays.asList(b1, b2);
A a2 = new A();
a2.id = "a2";
a2.matches = new ArrayList<B>();
A a3 = new A();
a3.id = "a3";
a3.matches = null;
template.save(a1);
template.save(a2);
template.save(a3);
QA qa = QA.a;
BooleanExpression expr = qa.matches.isNotEmpty();
Iterable<A> result = aRepo.findAll(expr);
assertThat(result, is(not((emptyIterable()))));
}
When I execute this test I get the following error:
com.mongodb.MongoException: Can't canonicalize query: BadValue $or needs an array
at com.mongodb.QueryResultIterator.throwOnQueryFailure(QueryResultIterator.java:214)
at com.mongodb.QueryResultIterator.init(QueryResultIterator.java:198)
at com.mongodb.QueryResultIterator.initFromQueryResponse(QueryResultIterator.java:176)
at com.mongodb.QueryResultIterator.<init>(QueryResultIterator.java:64)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:86)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:66)
at com.mongodb.DBCursor._check(DBCursor.java:458)
at com.mongodb.DBCursor._hasNext(DBCursor.java:546)
at com.mongodb.DBCursor.hasNext(DBCursor.java:571)
at com.mysema.query.mongodb.MongodbQuery.list(MongodbQuery.java:253)
at org.springframework.data.mongodb.repository.support.QueryDslMongoRepository.findAll(QueryDslMongoRepository.java:102)
You can try this for yourself at https://github.com/tedp/spring-querydsl-test
Am I doing something wrong?
This issue has now been fixed in version 3.6.3 of QueryDSL. See https://github.com/querydsl/querydsl/issues/1264 for further info.
Many thanks to all those who were involved in the fix!

limits using AggregateResult in batch APEX

I've just recently run into a governor limit using Batch Apex with the AggregateResult object and I was hoping someone could clearly explain this limit. I think batch APEX could support up to 50 million records, but when I use AggregateResult, I hit governor limits even though the total records in the object was about 250,000.
How does the AggregateResult limit apply within batch APEX?
Any explanation on how this limit is applied and how to overcome it would be appreciated. Should I simply avoid using AggregateResult within batch APEX? Or, if I do use it, how do I determine when this limit will be hit?
Here is a simple batch program I wrote using AggregateResult. This runs monthly and I'm aggregating Tasks related to a case by user. How can I determine if this will hit limits?
global class MemberCaseAchievementBatch implements Database.Batchable<sObject> {
public string query = 'select Id, Enhancement_Value__c from Case where RecordType.DeveloperName = \'SF_Enhancement\' AND Stage__c = \'Deployed\' AND ClosedDate = THIS_MONTH';
public Set<Id> caseIds = new Set<Id>();
public Set<Id> allCaseIds = new Set<Id>();
Integer totalCompletedCases = 0;
Integer totalAssignedCases = 0;
Double totalCompletedValue = 0;
global database.querylocator start(Database.BatchableContext BC)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, Sobject[] scope)
{
for(Case c : (List<Case>)scope) {
caseIds.add(c.Id);
totalCompletedCases++;
if(c.Enhancement_Value__c == null) {
totalCompletedValue = c.Enhancement_Value__c;
}
else {
totalCompletedValue += c.Enhancement_Value__c;
}
}
for(Case c : [select Id from Case where RecordType.DeveloperName = 'SF_Enhancement' AND CreatedDate = THIS_MONTH]) {
allCaseIds.add(c.Id);
totalAssignedCases++;
}
Map<Id, Member_Goal__c> memberGoalsMap = getMemberGoals();
List<Member_Case_Achievement__c> achievementsToInsert = new List<Member_Case_Achievement__c>();
AggregateResult[] results = getMemberAchievementMetrics(caseIds);
Map<String, AggregateResult> tasksAssigned = getMemberTotalTasksAssigned(allCaseIds);
Date firstDayOfMonth = System.today().toStartOfMonth();
Date lastDayOfMonth = firstDayOfMonth.addDays(Date.daysInMonth(firstDayOfMonth.year(), firstDayOfMonth.month()) - 1);
for(AggregateResult ar : results) {
Member_Case_Achievement__c mca = new Member_Case_Achievement__c();
if(memberGoalsMap.containsKey(String.valueOf(ar.get('OwnerId')))) mca.Member_Goal__c = memberGoalsMap.get(String.valueOf(ar.get('OwnerId'))).Id;
AggregateResult aggResult = tasksAssigned.get(String.valueOf(ar.get('OwnerId')));
mca.Total_Assigned_Tasks__c = (Integer)aggResult.get('expr0');
mca.Total_Completed_Tasks__c = (Integer)ar.get('expr1');
mca.Total_Completed_Task_Value__c = (Double)ar.get('expr0');
mca.Total_Assigned_Cases__c = totalAssignedCases;
mca.Total_Completed_Cases__c = totalCompletedCases;
mca.Total_Completed_Value__c = totalCompletedValue;
mca.Period_Start_Date__c = firstDayOfMonth;
mca.Period_End_Date__c = lastDayOfMonth;
achievementsToInsert.add(mca);
}
if(!achievementsToInsert.isEmpty() && achievementsToInsert.size() > 0) {
insert achievementsToInsert;
}
}
global void finish(Database.BatchableContext BC) {
}
private AggregateResult[] getMemberAchievementMetrics(Set<Id> caseids) {
AggregateResult[] groupedResults = [select OwnerId, SUM(Task_Value__c), Count(Id) from Task where WhatId in: caseids AND Subject in ('Requirements','Design','Develop / Config','Unit Testing') group by OwnerId];
return groupedResults;
}
private Map<String, AggregateResult> getMemberTotalTasksAssigned(Set<Id> caseids) {
Map<String, AggregateResult> aggregateResultMap = new Map<String, AggregateResult>();
for(AggregateResult ar : [select OwnerId, Count(Id) from Task where WhatId in: caseids AND Subject in ('Requirements','Design','Develop / Config','Unit Testing') group by OwnerId]) {
aggregateResultMap.put(String.valueOf(ar.get('OwnerId')), ar);
}
return aggregateResultMap;
}
private Map<Id, Member_Goal__c> getMemberGoals() {
Map<Id, Member_Goal__c> memberGoalMap = new Map<Id, Member_Goal__c>();
for(Member_Goal__c mg : [select Id, Member__c from Member_Goal__c where Goal_Period__r.Period_Start_Date__c = THIS_MONTH]) {
memberGoalMap.put(mg.Member__c, mg);
}
return memberGoalMap;
}
}
Is the limit determined by the scope? Or, is it determined by the AggregateResult query?
Batch apex allows you to run batches of up to 50 million records. So in your case you could batch over 50M Cases. Normal Apex limits still apply however inside of each batch.
You could try to limit the scope size of each batch via Database.executeBatch(yourBatch, scopeSize); where scopeSize is an Integer between 1 and 2000. Setting this to a lower number (200 is the default) would run fewer Cases per batch, in effect hopefully decreasing the size of your aggregate result too.

Resources