Deleting a Collection with NHibernate Using the Criteria API - nhibernate

I think I know what the answer to this question is probably going to be, but I thought I'd go ahead and ask it anyway.
It appears that within NHibernate if I do something like this:
IList<Customer> customers = Session.CreateCriteria(typeof(Customer))
.Add(Restrictions.Eq("Name", "Steve")
.List<Customer>();
And I want to then delete that list of customers. From what I can tell the only way to do it is like this:
foreach(var customer in customers)
{
Session.Delete(customer);
}
But what I'm wondering is if there's any way I can just go:
Session.Delete(customers);
And delete the entire collection with a single call?

Not with Criteria, but it's easy to do with HQL:
session.CreateQuery("delete Customer customer where customer in (:customers)")
.SetParameterList("customers", customers.ToArray())
.ExecuteUpdate();
But you don't need to load them. You can also do it in one shot:
session.CreateQuery("delete Customer where Name = 'Steve'")
.ExecuteUpdate();

Related

Update aggregate root with child entities using repository pattern

I will use the standard example of Invoice and invoiceLineItems. So I have a single repository for the invoices because the invoice is the aggregate root. Creating a whole invoice and deleting an invoice is pretty easy. What about updating an invoice?
Maybe I shouldn't be thinking so "low level" but I can't imagine a nice way to handle this. Here is my pseudo code for this operation
public void Update(Invoice inv)
{
var dbVersion = GetInvoice(id);
foreach(var lineItem in inv.LineItems)
{
if (lineItem not in dbVersion)
{
InsertLineItem(lineItem);
}
else
{
UpdateLineItem(lineItem);
}
}
foreach(var lineItem in dbVersion)
{
if (lineItem not in inv)
{
DeleteLineItem(lineItem);
}
}
}
I am imagining the queries this thing needs to produce and it seems very inefficient.
Select statements to re materialize the whole invoice.
Update statement for the whole invoice (regardless if anything on the invoice changed)
Insert statement for each line item that is new
Update statement for each line item that is already there (regardless if anything changed)
Delete statement for each line item that is missing
Of course you wouldn't have to generate an update statement for the whole invoice and for the InvoiceLineItem but that would require you to check all the properties in each.
If you however went the IvoiceLineItemRepo you would only update when a user performed an action that would necessitate the update. So if the user updated just a line then there would only be a single update and for that line item and you wouldn't have to check for changes you could assume that it had changed.
Is there a nicer way of handling the updates?
You can first delete LineItem collection and then insert new collection like all-delete-orphan in hibernate.
deleteAllLineItemsByInvoiceId(Long id);
insertAllLineItems(List<LineItem> lineItems);

Eloquent many-to-many with filters on both sides

I've been reading the docs but I"m not sure how to do this. http://doc.laravelbook.com/eloquent/#many-to-many
Say I have a users, roles, and a pivot table.
I have belongsToMany set up for both Role and User
In a controller, I want to get a user_id and return what roles they have of a specific type only.
(There is also a role type table, but I can work with the IDs directly).
I start something like this
$specific_type_role = Role::where('role_type_id', 3)::where(?$user_id?)
//need to involve
$circle_users = RoleUser::where('user_id', $user_id)->get();
but I think it should be able to be done automatically. don't know how to include the filter right in the query.
Not sure if it's that what you need but, you probably will be able to do something like that:
public function getAdminRoles()
{
$user = User::find(1);
return $user->roles()->where('role_type_id', 1)->get();
}

Zend db cascade delete multiple levels

How does one make Zend Db cascade delete multiple levels of the hierarchy? For example:
dealers -> products -> attributes
deleting one dealer should go all the way down to attributes, and now it doesn't :(
Any thoughts?
On row of the Zend_Table_Abstract within the function _cascadeDelete a row is constructed like this:
$rowsAffected += $this->delete($where);
It should instead be constructed as something like this:
$toDelete = $this->fetchAll($where);
foreach($toDelete as $row) {
$rowsAffected += $row->delete();
}
More info here.
It's worked for me in one cause but need to test more.

Where to expose the results of a specific SQL query in the domain model

Which of these examples would be the best way to expose a collection of Orders for a specific Person that contain a specific Product and why? Or maybe there is a better way to do this alltogether? (Sorry I am new to domain modeling). The Orders list is pulled from the database with an SQL query and turned into a List collection.
A Person has 1 to many Orders and an Order has 1 to many Products.
1)
class Person
{
List OrdersContaining(Product p)
{.....}
}
2)
class Order
{
List ForPersonContainingProduct(Person person, Product product)
{.....}
}
2)
class Product
{
List OrdersFor(Person p)
{.....}
}
I would not expose such a method directly on the domain object itself, which encapsulates the data. Rather, I would use the DAO pattern applied to the Order domain. Which, in essence, is a variation of your #2:
class OrderDAO {
List<Order> listByPersonAndProduct(Person person, Product product){
.....
}
}
This way, the various patterns of access that you need to add over time are separated from the Order domain object.
Person could still have a .Orders collection which has all their orders. Then it becomes a question of lazy loading an populating this collection when you know you're going to need it. Something like N/Hibernate helps a lot here.

NHibernate projection help

Im having a problem creating a projection for my nhibernate detachedcriteria object.
I have a class Spa which is linked to table Address.
Address has a field called City which is a string.
public class Spa : IAggregateRoot
{
[BelongsTo("AddressID", Cascade = CascadeEnum.All)]
public Address Address { get; set; }
}
My ultimate goal is to get a distinct list of City names.
If i could get all spas with distinct cities i would be happy too.
All my attempts have been for naught and havent found any helpful posts.
So far i've tried:
DetachedCriteria query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
query.SetProjection(
Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Alias(Projections.Property("Address"), "A"))));
var Spas = ActiveRecordMediator<Spa>.FindAll(query);
I know the above is not correct, just trying to find somewhere to start.
Any help would be appreciated.
Also any simple projections tutorials would be appreciated, cant seem to find anything straight forward out there.
I also tried, but got cast error, looking into it:
DetachedCriteria query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
.SetProjection(Projections.Distinct(Projections.Property("A.City")));
It seems to me there are two parts to your question.
1. What should my DetachedCriteria look like?
If you are not performing any other aggregations, GROUP BY should provide the same results as DISTINCT. This is the query I would use:
var query = DetachedCriteria.For<Spa>()
.CreateAlias("Address", "A")
.SetProjection(Projections.GroupProperty("A.City"));
2. How do I execute it with Castle ActiveRecord?
I have never used ActiveRecord, but based on the method signatures, I would expect something like this to work:
var cities = ActiveRecordMediator<string>.FindAll(query);
If you have access to the NHibernate session, you could also execute it this way:
var cities = query.GetExecutableCriteria(session).List<string>();