Skip to content

Commit 333eed6

Browse files
gliljashazzik
authored andcommitted
NH-3488 - Strongly typed Inserts, Updates and Deletes (#391)
1 parent 675ec72 commit 333eed6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+3470
-53
lines changed

doc/reference/modules/batch.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ session.Close();]]></programlisting>
133133
(DML) statements: <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
134134
data directly in the database will not affect in-memory state. However, NHibernate provides methods
135135
for bulk SQL-style DML statement execution which are performed through the
136-
Hibernate Query Language (<link linkend="queryhql">HQL</link>).
136+
Hibernate Query Language (<link linkend="queryhql">HQL</link>). A
137+
<link linkend="querylinq-modifying">Linq implementation</link> is available too.
137138
</para>
138139

139140
<para>

doc/reference/modules/query_linq.xml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,119 @@ IList<Cat> oldCats =
467467
.ToList();]]></programlisting>
468468
</sect1>
469469

470+
<sect1 id="querylinq-modifying">
471+
<title>Modifying entities inside the database</title>
472+
473+
<para>
474+
Beginning with NHibernate 5.0, Linq queries can be used for inserting, updating or deleting entities.
475+
The query defines the data to delete, update or insert, and then <literal>Delete</literal>,
476+
<literal>Update</literal>, <literal>UpdateBuilder</literal>, <literal>InsertInto</literal> and
477+
<literal>InsertBuilder</literal> queryable extension methods allow to delete it,
478+
or instruct in which way it should be updated or inserted. Those queries happen entirely inside the
479+
database, without extracting corresponding entities out of the database.
480+
</para>
481+
<para>
482+
These operations are a Linq implementation of <xref linkend="batch-direct" />, with the same abilities
483+
and limitations.
484+
</para>
485+
486+
<sect2 id="querylinq-modifying-insert">
487+
<title>Inserting new entities</title>
488+
<para>
489+
<literal>InsertInto</literal> and <literal>InsertBuilder</literal> method extensions expect a NHibernate
490+
queryable defining the data source of the insert. This data can be entities or a projection. Then they
491+
allow specifying the target entity type to insert, and how to convert source data to those target
492+
entities. Three forms of target specification exist.
493+
</para>
494+
<para>
495+
Using projection to target entity:
496+
</para>
497+
<programlisting><![CDATA[session.Query<Cat>()
498+
.Where(c => c.BodyWeight > 20)
499+
.InsertInto(c => new Dog { Name = c.Name + "dog", BodyWeight = c.BodyWeight });]]></programlisting>
500+
<para>
501+
Projections can be done with an anonymous object too, but it requires supplying explicitly the target
502+
type, which in turn requires re-specifying the source type:
503+
</para>
504+
<programlisting><![CDATA[session.Query<Cat>()
505+
.Where(c => c.BodyWeight > 20)
506+
.InsertInto<Cat, Dog>(c => new { Name = c.Name + "dog", BodyWeight = c.BodyWeight });]]></programlisting>
507+
<para>
508+
Or using assignments:
509+
</para>
510+
<programlisting><![CDATA[session.Query<Cat>()
511+
.Where(c => c.BodyWeight > 20)
512+
.InsertBuilder()
513+
.Into<Dog>()
514+
.Value(d => d.Name, c => c.Name + "dog")
515+
.Value(d => d.BodyWeight, c => c.BodyWeight)
516+
.Insert();]]></programlisting>
517+
<para>
518+
In all cases, unspecified properties are not included in the resulting SQL insert.
519+
<link linkend="mapping-declaration-version"><literal>version</literal></link> and
520+
<link linkend="mapping-declaration-timestamp"><literal>timestamp</literal></link> properties are
521+
exceptions. If not specified, they are inserted with their <literal>seed</literal> value.
522+
</para>
523+
<para>
524+
For more information on <literal>Insert</literal> limitations, please refer to
525+
<xref linkend="batch-direct" />.
526+
</para>
527+
</sect2>
528+
529+
<sect2 id="querylinq-modifying-update">
530+
<title>Updating entities</title>
531+
<para>
532+
<literal>Update</literal> and <literal>UpdateBuilder</literal> method extensions expect a NHibernate
533+
queryable defining the entities to update. Then they allow specifying which properties should be
534+
updated with which values. As for insertion, three forms of target specification exist.
535+
</para>
536+
<para>
537+
Using projection to updated entity:
538+
</para>
539+
<programlisting><![CDATA[session.Query<Cat>()
540+
.Where(c => c.BodyWeight > 20)
541+
.Update(c => new Cat { BodyWeight = c.BodyWeight / 2 });]]></programlisting>
542+
<para>
543+
Projections can be done with an anonymous object too:
544+
</para>
545+
<programlisting><![CDATA[session.Query<Cat>()
546+
.Where(c => c.BodyWeight > 20)
547+
.Update(c => new { BodyWeight = c.BodyWeight / 2 });]]></programlisting>
548+
<para>
549+
Or using assignments:
550+
</para>
551+
<programlisting><![CDATA[session.Query<Cat>()
552+
.Where(c => c.BodyWeight > 20)
553+
.UpdateBuilder()
554+
.Set(c => c.BodyWeight, c => c.BodyWeight / 2)
555+
.Update();]]></programlisting>
556+
<para>
557+
In all cases, unspecified properties are not included in the resulting SQL update. This could
558+
be changed for <link linkend="mapping-declaration-version"><literal>version</literal></link> and
559+
<link linkend="mapping-declaration-timestamp"><literal>timestamp</literal></link> properties:
560+
using <literal>UpdateVersioned</literal> instead of <literal>Update</literal> allows incrementing
561+
the version. Custom version types (<literal>NHibernate.Usertype.IUserVersionType</literal>) are
562+
not supported.
563+
</para>
564+
<para>
565+
When using projection to updated entity, please note that the constructed entity must have the
566+
exact same type than the underlying queryable source type. Attempting to project to any other class
567+
(anonymous projections excepted) will fail.
568+
</para>
569+
</sect2>
570+
571+
<sect2 id="querylinq-modifying-delete">
572+
<title>Deleting entities</title>
573+
<para>
574+
<literal>Delete</literal> method extension expects a queryable defining the entities to delete.
575+
It immediately deletes them.
576+
</para>
577+
<programlisting><![CDATA[session.Query<Cat>()
578+
.Where(c => c.BodyWeight > 20)
579+
.Delete();]]></programlisting>
580+
</sect2>
581+
</sect1>
582+
470583
<sect1 id="querylinq-querycache">
471584
<title>Query cache</title>
472585

src/NHibernate.Test/Hql/Ast/BulkManipulation.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ public void SimpleInsert()
7070
data.Cleanup();
7171
}
7272

73+
[Test]
74+
public void SimpleInsertFromAggregate()
75+
{
76+
var data = new TestData(this);
77+
data.Prepare();
78+
79+
ISession s = OpenSession();
80+
ITransaction t = s.BeginTransaction();
81+
82+
s.CreateQuery("insert into Pickup (id, Vin, Owner) select id, max(Vin), max(Owner) from Car group by id").ExecuteUpdate();
83+
84+
t.Commit();
85+
t = s.BeginTransaction();
86+
87+
s.CreateQuery("delete Vehicle").ExecuteUpdate();
88+
89+
t.Commit();
90+
s.Close();
91+
92+
data.Cleanup();
93+
}
94+
95+
7396
[Test]
7497
public void InsertWithManyToOne()
7598
{
@@ -92,6 +115,31 @@ public void InsertWithManyToOne()
92115
data.Cleanup();
93116
}
94117

118+
[Test]
119+
public void InsertWithManyToOneAsParameter()
120+
{
121+
var data = new TestData(this);
122+
data.Prepare();
123+
124+
ISession s = OpenSession();
125+
ITransaction t = s.BeginTransaction();
126+
127+
var mother = data.Butterfly;
128+
129+
s.CreateQuery(
130+
"insert into Animal (description, bodyWeight, mother) select description, bodyWeight, :mother from Human")
131+
.SetEntity("mother",mother)
132+
.ExecuteUpdate();
133+
134+
t.Commit();
135+
t = s.BeginTransaction();
136+
137+
t.Commit();
138+
s.Close();
139+
140+
data.Cleanup();
141+
}
142+
95143
[Test]
96144
public void InsertWithMismatchedTypes()
97145
{
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace NHibernate.Test.LinqBulkManipulation.Domain
2+
{
3+
public class Address
4+
{
5+
private string street;
6+
private string city;
7+
private string postalCode;
8+
private string country;
9+
private StateProvince stateProvince;
10+
11+
public string Street
12+
{
13+
get { return street; }
14+
set { street = value; }
15+
}
16+
17+
public string City
18+
{
19+
get { return city; }
20+
set { city = value; }
21+
}
22+
23+
public string PostalCode
24+
{
25+
get { return postalCode; }
26+
set { postalCode = value; }
27+
}
28+
29+
public string Country
30+
{
31+
get { return country; }
32+
set { country = value; }
33+
}
34+
35+
public StateProvince StateProvince
36+
{
37+
get { return stateProvince; }
38+
set { stateProvince = value; }
39+
}
40+
}
41+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.LinqBulkManipulation.Domain
4+
{
5+
public class Animal
6+
{
7+
private long id;
8+
private float bodyWeight;
9+
private ISet<Animal> offspring;
10+
private Animal mother;
11+
private Animal father;
12+
private string description;
13+
private Zoo zoo;
14+
private string serialNumber;
15+
16+
public virtual long Id
17+
{
18+
get { return id; }
19+
set { id = value; }
20+
}
21+
22+
public virtual float BodyWeight
23+
{
24+
get { return bodyWeight; }
25+
set { bodyWeight = value; }
26+
}
27+
28+
public virtual ISet<Animal> Offspring
29+
{
30+
get { return offspring; }
31+
set { offspring = value; }
32+
}
33+
34+
public virtual Animal Mother
35+
{
36+
get { return mother; }
37+
set { mother = value; }
38+
}
39+
40+
public virtual Animal Father
41+
{
42+
get { return father; }
43+
set { father = value; }
44+
}
45+
46+
public virtual string Description
47+
{
48+
get { return description; }
49+
set { description = value; }
50+
}
51+
52+
public virtual Zoo Zoo
53+
{
54+
get { return zoo; }
55+
set { zoo = value; }
56+
}
57+
58+
public virtual string SerialNumber
59+
{
60+
get { return serialNumber; }
61+
set { serialNumber = value; }
62+
}
63+
64+
public virtual void AddOffspring(Animal offSpring)
65+
{
66+
if (offspring == null)
67+
{
68+
offspring = new HashSet<Animal>();
69+
}
70+
71+
offspring.Add(offSpring);
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)