Inserting parent/child rows in Hibernate with autoincrement index throws error - scala

I have the following Hibernate classes in Scala, where one Group has many Items. Note that the #Id of the Group class has an autoincrement annotation.
#Entity
#Table(name = "items")
class Item extends Serializable {
#Id
#ManyToOne
#JoinColumn(name="group_sk", nullable=false)
var group: Group = _
#Id
var index: Int = _
var name: String = _
def canEqual(a: Any) = a.isInstanceOf[Item]
override def equals(that: Any): Boolean =
that match {
case that: Item => that.canEqual(this) && this.hashCode == that.hashCode
case _ => false
}
override def hashCode: Int = {
val prime = 31
var result = 1
result = prime * result + group.sk;
result = prime * result + index
return result
}
}
#Entity
#Table(name = "groups")
class Group {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "group_generator")
#SequenceGenerator(name="group_generator",
sequenceName = "GroupSeq", allocationSize = 1)
var sk: Int = _
#Column(name = "group_name")
var name: String = _
#OneToMany
#JoinColumn(name="group_sk")
var items: java.util.List[Item] = _
}
I try to insert one group with a related item, where both the Group should have an auto incremented Id:
session.beginTransaction
val group = new Group
group.name = "Group name"
group.items = new java.util.ArrayList[Item]()
val item1 = new Item
item1.group = group
item1.index = 1
item1.name = "Item 1"
group.items.add(item1)
session.save(group)
session.getTransaction.commit
The exception I get is
Caused by: javax.persistence.OptimisticLockException: Batch update
returned unexpected row count from update [0]; actual row count: 0;
expected: 1
And the Hibernate sql log shows:
Hibernate: select GroupSeq.nextval from dummy
Hibernate: insert into groups (group_name, sk) values (?, ?)
Hibernate: update items set group_sk=? where index=? and group_sk=?
Note that the last update statement doesn't make sense because you won't update the value of group_sk where the column is also in the condition. Moreover, there's no insert statement of the items table.
How to fix this problem?

You need to set cascade type on your OneToMany relationship to tell Hibernate how to mange Items collection of the Group class. So for example something like this should work:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="group_sk")
var items: java.util.List[Item] = _

Related

Return Django ORM union result in Django-Graphene

I am trying to query two separate objects and return them as a single result set. I've tried using a Union and an Interface, but not sure what I'm doing wrong there.
My model:
class BaseActivity(models.Model):
class Meta:
abstract = True
name = models.CharField(db_column="activity_type_name", unique=True, max_length=250)
created_at = CreationDateTimeField()
modified_at = ModificationDateTimeField()
created_by = models.UUIDField()
modified_by = models.UUIDField()
deleted_at = models.DateTimeField(blank=True, null=True)
deleted_by = models.UUIDField(blank=True, null=True)
class Activity(BaseActivity):
class Meta:
db_table = "activity_type"
ordering = ("sort_order",)
id = models.UUIDField(
db_column="activity_type_id",
primary_key=True,
default=uuid.uuid4,
editable=False,
)
sort_order = models.IntegerField()
def __str__(self):
return self.name
class CustomActivity(BaseActivity):
class Meta:
db_table = "organization_custom_activity_type"
id = models.UUIDField(
db_column="organization_custom_activity_type",
primary_key=True,
default=uuid.uuid4,
editable=False,
)
farm = models.ForeignKey("farm.Farm", db_column="organization_id", on_delete=models.DO_NOTHING, related_name="custom_activity_farm")
My schema:
class FarmActivities(graphene.ObjectType):
id = graphene.String()
name = graphene.String()
class ActivityType(DjangoObjectType):
class Meta:
model = Activity
fields = ("id", "name", "requires_crop", "sort_order")
class CustomActivityType(DjangoObjectType):
class Meta:
model = CustomActivity
fields = ("id", "name", "farm")
And the query:
class Query(graphene.ObjectType):
get_farm_activities = graphene.Field(FarmActivities, farm=graphene.String(required=True))
def resolve_get_farm_activities(self, info, farm):
farm_activities = Activity.objects.values("id", "name").filter(
farmtypeactivityrel__farm_type__farm=farm, deleted_at=None
)
custom_activities = CustomActivity.objects.values("id", "name").filter(farm=farm, deleted_at=None)
return list(chain(farm_activities, custom_activities))
With this, I do get a list back from the query, but it's not going thru the resolver when I call getFarmActivities.
Literally the list returns:
ExecutionResult(data={'getFarmActivities': {'id': None, 'name': None}}, errors=None)
How to resolve graphene.Union Type?
That provided the hint I needed to get this working. I had to build a Union that would parse the model and not the schema type.
class FarmActivities(graphene.Union):
class Meta:
types = (ActivityType, CustomActivityType)
#classmethod
def resolve_type(cls, instance, info):
if isinstance(instance, Activity):
return ActivityType
if isinstance(instance, CustomActivity):
return CustomActivityType
return FarmActivities.resolve_type(instance, info)
Which then allowed me to run the query like so:
def resolve_get_farm_activities(self, info, farm):
farm_activities = Activity.objects.filter(
farmtypeactivityrel__farm_type__farm=farm
)
custom_activities = CustomActivity.objects.filter(farm=farm)
return list(chain(farm_activities, custom_activities))
And the API query:
query farmParam($farm: String!)
{
getFarmActivities(farm: $farm)
{
... on CustomActivityType
{
id
name
}
... on ActivityType
{
id
name
}
}
}

How to loop through dbcontext all dbset in Entity Framework Core to get count?

I have 20 Dbsets in my context which I want to get the row count for each dbset to make sure all dbset row count is 0. To get the count for one dbset, this is my code:
var person = context.Persons.Count();
Is there a way to loop through the context, get the count for each dbset dynamically?
There is solution. Usage is simple:
var tablesinfo = ctx.GetTablesInfo();
if (tablesinfo != null)
{
var withRecords = tablesinfo
.IgnoreQueryFilters()
.Where(ti => ti.RecordCount > 0)
.ToArray();
}
Extension returns IQueryable<TableInfo> and you can reuse this query later. Probably you will need to filter out Views, but I think you can handle that. Note that IgnoreQueryFilters can be important if you have Global Query Filters defined.
What extension do:
It scans Model for entity types registered for particular DbContext and generates big Concat of Count queries. Here we have to do that via grouping by constant value.
Schematically it will generate the following LINQ query:
var tablesinfo =
ctx.Set<Entity1>.GroupBy(e => 1).Select(g => new TableInfo { TableName = "Entity1", RecordCount = g.Count()})
.Concat(ctx.Set<Entity2>.GroupBy(e => 1).Select(g => new TableInfo { TableName = "Entity2", RecordCount = g.Count()}))
.Concat(ctx.Set<Entity3>.GroupBy(e => 1).Select(g => new TableInfo { TableName = "Entity3", RecordCount = g.Count()}))
...
Which wll be converted to the following SQL:
SELECT "Entity1" AS TableName, COUNT(*) AS RecordCount FROM Entity1
UNION ALL
SELECT "Entity2" AS TableName, COUNT(*) AS RecordCount FROM Entity2
UNION ALL
SELECT "Entity3" AS TableName, COUNT(*) AS RecordCount FROM Entity3
...
Implementation:
public static class QueryableExtensions
{
public class TableInfo
{
public string TableName { get; set; } = null!;
public int RecordCount { get; set; }
}
public static IQueryable<TableInfo> GetTablesInfo(this DbContext ctx)
{
Expression query = null;
IQueryProvider provider = null;
var ctxConst = Expression.Constant(ctx);
var groupingKey = Expression.Constant(1);
// gathering information for MemberInit creation
var newExpression = Expression.New(typeof(TableInfo).GetConstructor(Type.EmptyTypes));
var tableNameProperty = typeof(TableInfo).GetProperty(nameof(TableInfo.TableName));
var recordCountProperty = typeof(TableInfo).GetProperty(nameof(TableInfo.RecordCount));
foreach (var entityType in ctx.Model.GetEntityTypes())
{
var entityParam = Expression.Parameter(entityType.ClrType, "e");
var tableName = entityType.GetTableName();
// ctx.Set<entityType>()
var setQuery = Expression.Call(ctxConst, nameof(DbContext.Set), new[] {entityType.ClrType});
// here we initialize IQueryProvider, which is needed for creating final query
provider ??= ((IQueryable) Expression.Lambda(setQuery).Compile().DynamicInvoke()).Provider;
// grouping paraneter has generic type, we have to specify it
var groupingParameter = Expression.Parameter(typeof(IGrouping<,>).MakeGenericType(typeof(int), entityParam.Type), "g");
// g => new TableInfo { TableName = "tableName", RecordCount = g.Count() }
var selector = Expression.MemberInit(newExpression,
Expression.Bind(tableNameProperty, Expression.Constant(tableName)),
Expression.Bind(recordCountProperty,
Expression.Call(typeof(Enumerable), nameof(Enumerable.Count), new[] {entityParam.Type}, groupingParameter)));
// ctx.Set<entityType>.GroupBy(e => 1)
var groupByCall = Expression.Call(typeof(Queryable), nameof(Queryable.GroupBy), new[]
{
entityParam.Type,
typeof(int)
},
setQuery,
Expression.Lambda(groupingKey, entityParam)
);
// ctx.Set<entityType>.GroupBy(e => 1).Select(g => new TableInfo { TableName = "tableName", RecordCount = g.Count()}))
groupByCall = Expression.Call(typeof(Queryable), nameof(Queryable.Select),
new[] {groupingParameter.Type, typeof(TableInfo)},
groupByCall,
Expression.Lambda(selector, groupingParameter));
// generate Concat if needed
if (query != null)
query = Expression.Call(typeof(Queryable), nameof(Queryable.Concat), new[] {typeof(TableInfo)}, query,
groupByCall);
else
query = groupByCall;
}
// unusual situation, but Model can have no registered entities
if (query == null)
return null;
return provider.CreateQuery<TableInfo>(query);
}
}

EF Core - how to select count from child table based on foreign key

TableB has a field TableAId which is linked to the Id of TableA. I want to select the count from TableB based on TableAId like -
SELECT *,
(SELECT COUNT(*) FROM tableB where tableB.TableAId = tableA.Id) as count
FROM tableA
So far I have the code:
var data = _context.TableA.AsQueryable();
...
data = data.Select(l => l.TableAId= p.Id).Count();
But on the last line, p is not recognized as a variable.
How do I make this work?
EDIT :
my original query is quiet complex and already filtering data
var data = _context.TableA.AsQueryable();
data = data.Include(p => p.SomeClassA)
.Include(p => p.SomeClassB);
data = data.Where(p => p.Id == somevalue);
data = data.Where(p => p.SomeClassA.Name.Contains(someothervalue));
data = data.Where(p => p.SomeClassA.SomeField.Contains(yetanothervalue));
I tried adding this but it cannot compile
(TableAId & Count do not exist):
data = data.Join(
_context.TableB,
groupByQuery => groupByQuery.TableAId ,
TableA => TableA.Id,
(groupByQuery, TableAItem) => new
{
TableAId = groupByQuery.Id,
Count = groupByQuery.Count,
TableAItem = TableAItem
}
);
If you are just interested in count, then do the following:
var data = _context.TableB.AsQueryable();
var groupByCountQuery = data.GroupBy(a=>a.TableAId, (tableAId, tableBItems) => new
{
TableAId = tableAId,
Count = tableBItems.Count()
});
var result = groupByCountQuery.ToList(); // or change to ToListAsync()
This will give you the count based on TableAId.
If you need the tableA items as well in the result, following ca be done:
var groupByCountQuery = data.GroupBy(a=>a.TableAId, (tableAId, tableBItems) => new
{
TableAId = tableAId,
Count = tableBItems.Count()
}).Join(_context.TableA,
groupByQuery => groupByQuery.TableAId,
tableA => tableA.Id,
(groupByQuery , tableA) => new {
TableAId = groupByQuery.TableAId,
Count = groupByQuery.Count,
TableAItem = tableA
} );
Let's say TableA has properties - Id, FieldA1 and FieldA2.
First, you have to include TableB in your query so that you can take its count, like -
var data = _context.TableA.Include(p=> p.TableB).AsQueryable();
Then in the Select method you have to create a new object with TableA's properties and TableB's count, like -
var list = data.Select(p =>
new
{
Id = p.Id,
A1 = p.FieldA1,
A2 = p.FieldA2,
Count = p.OrderLines.Count
}).ToList();
Notice, this is assigned to a new variable list. That is because it does not return a list of TableA, it returns a list of an anonymous object with properties - Id, A1, A2 and Count. Therefore, you cannot assign it to the previously declared data variable, because data is of type IQueryable<TableA>.
Alternatively, you can declare a class to hold the data values, like -
public class MyData
{
public int Id { get; set; }
public string A1 { get; set; }
public string A2 { get; set; }
public int Count { get; set; }
}
and use it like -
var list = data.Select(p =>
new MyData
{
Id = p.Id,
A1 = p.FieldA1,
A2 = p.FieldA2,
Count = p.OrderLines.Count
}).ToList();
If you want to start from TableA, you can use the following linq query:
var data = _context.TableAs.AsQueryable();
var x = (from a in data
join b in _context.TableBs on a.Id equals b.TableAId
group a.TableB by a.Id into g
select new
{
TableAId = g.Key,
TableBItem = g.FirstOrDefault(),
Count = g.Count()
}).ToList();
Result:

How to write JPQL query using multiple members of same collection?

I have a noticication entity that has OneToMany relationship with its parameters, which is a list of NotificationParamEntity objects.
The code for both classes looks like:
// Notification Entity
#Entity
#Table (name = "NOTIFICATIONS")
public class NotificationEntity {
......
#OneToMany (mappedBy = "notification")
private List<NotificationParamEntity> params;
......
}
// Notification Parameter Entity
#Entity
#Table (name = "NOTIFICATIONS_PARAM")
public class NotificationParamEntity {
......
#Column (name = "KEY", length = 40, nullable = false)
#Enumerated (EnumType.STRING)
private NotificationParameterEnum key;
#Column (name = "VALUE", length = 4000, nullable = false)
private String value;
#ManyToOne
#JoinColumn (name = "NOTIFICATION_ID", nullable = false)
private NotificationEntity notification;
......
}
Now I can use the query below to get the notification that has a parameter named "P1" and with a value "V1":
SELECT DISTINCT anEntity FROM NotificationEntity anEntity, IN
(anEntity.params) p WHERE p.key = "P1" AND p.value = 'V1'
But when I want to find out the notification that has two specified parameters(P1=V1 and P2=V2), my query below failed to find anything:
SELECT DISTINCT anEntity FROM NotificationEntity anEntity, IN
(anEntity.params) p WHERE p.key = "P1" AND p.value = 'V1' AND p.key = "P2" AND p.value = "V2"
I can understand why it doesn't work: there is no parameter that can have two different keys, so the query return nothing.
But how to make this work? Assume I have a notification entity that has two parameters, one is named P1 and value is V1, the other one is P2 and the value is V2, how can I find this notification entity with JPQL query?
Try something like this:
SELECT n FROM NotificationEntity n WHERE EXISTS
(SELECT p FROM NotificationParamEntity p WHERE p.key = 'P1' AND p.value = 'V1'
AND p.notificationEntity = n)
AND EXISTS
(SELECT p2 FROM NotificationParamEntity p2 WHERE p2.key = 'P2' AND p2.value = 'V2'
AND p2.notificationEntity = n)
Note that it requires a reference from NotificationParamEntity to NotificationEntity (I don't see that column in the snippet of your code, but you should have it, a #ManyToOne).

Linq to Nhibernate with Contains and SubString does not work?

There appears to be mismatch betweeen the SQL generated by NHibernate and SQL expected by SQL2008 in the following case:
public void PersistPerson()
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using(var transaction = session.BeginTransaction())
{
session.Save(new Person {FirstName = "Foo", LastName = "Bar"});
session.Save(new Person {FirstName = "Foo", LastName = "Dah"});
session.Save(new Person {FirstName = "Foo", LastName = "Wah"});
transaction.Commit();
}
}
using (var session = sessionFactory.OpenSession())
{
using(var transaction = session.BeginTransaction())
{
var queryable = from p in session.Query<Person>() select p;
var lastNames = new[]{"B", "D"};
var result = queryable.Where(r => lastNames.Contains(r.LastName.Substring(0, 1))).ToList();
transaction.Commit();
Assert.That(result[0].LastName, Is.EqualTo("Bar"));
}
}
}
The resulting sql query generated by NHibernate for
var result = queryable.Where(r => lastNames.Contains(r.LastName.Substring(0, 1))).ToList();
is:
select person0_.Id as Id0_,
person0_.FirstName as FirstName0_,
person0_.LastName as LastName0_ from [Person] person0_ where upper(substring(person0_.LastName,
0 /* #p0 */,
1 /* #p1 */)) in ('B' /* #p2 */)
From the MSDN documentation for T-SQL SUBSTRING
http://msdn.microsoft.com/en-us/library/ms187748.aspx
SUBSTRING (value_expression ,start_expression ,length_expression )
although the documentation says otherwise, from the comments posted start_expression appears to be 1 - based (not 0 indexed)
For example:
SQL: SELECT x = SUBSTRING('abcdef', 0, 3);
RESULT: x = 'ab'
and NOT x = 'abc'
Any thoughts on how I can get around this ?
I think it's a bug. just change your codes to r.LastName.Substring(1, 1) and it works (resulting sql will be substring(1,1)).