Grails: Updating data in relationship - grails

I've started a little project using Grails 2.2.3 and I'm now wondering what's the right/most efficient way to update a model with relationship.
class Computer extends Inventory {
String hostName
...
static hasMany = [memories: Memory]
}
class Memory extends Inventory {
Integer capacity
String bank
...
static belongsTo = [computer: Computer]
}
Data input is scheduled regularly from external source, no user interacts with a view.
When just doing a mem.each { computer.addToMemories(it) } there will be a duplicate Memory entry for each scheduled run. I can solve this be checking if a corresponding entry exists:
mem.each {
if (!Memory.findWhere(it)) computer.addToMemories(it)
}
This would work if new mem modules are added to a computer. But imagine, modules are removed from a computer. You could create a nested loop through saved and given modules and compare them. But that's some coding and not very nice. Or you could first do a removeAll() and let then add all modules as new ones. But that's not very nice, too. And id would grow very fast.
Do you know a smarter way to handle this situation? Isn't there a possibility to say "Hey model, here is the latest set of mem modules for this computer. Add it if it's an new one, update it if has changed, or delete it if it has been removed from the computer." Or: "Update your information to this set."
Any ideas?
Thanks in advance,
Chris

Related

Grails connect database fields

I'm trying to learn Grails but am still pretty much on beginner level.
I made a tiny application, where you can basically add events, and people can write reviews about them and rate them.
So I have an eventController and reviewController. The rating is just an Integer in a review. But now I want to show an overall rating for an event. So the events rating should be the average value of the corresponding ratings value.
This is my event domain code where the rating is initially set, I left out the constraints and toString:
class Event {
Double dRating = 2
static hasMany = [ratings:Rating]
}
The controller is simply:
class EventController {
def scaffold = Event
}
The rating domain file:
class Rating {
String comment
java.util.Date date = new java.util.Date()
Integer rating
Event event
}
and the Rating Controller is:
class RatingController {
def scaffold = Rating
}
Hope I didn't make mistakes, I had to translate variable names so they're understandable.
I'm guessing that where dRating is set, I could somehow add some calculation, but I don't even know how to access the values of the rating, and everything I try ends in lots of errors and having to restart the app again and again. I tried adding calculations in the controller, but there I don't know how to get the value into the database. So where do I actually put the logic, and how do I access the values?
If any other file is important, please tell me.
Hoping to get some hints on how to start doing this, I didn't think it would be this hard.
At first I would recommend start reading grails doc. Later from your question it is not much clear what you are asking a there could be several places or possibilities of setting up the value in domain field and persist it to database. I'm telling all of these which are known to me:
If it is generalised calculation that needs to be applied to your dRating field, then create a setter with standard bean naming convention and do this there. For example, you want to find percentage from say 1000 and then add it to dRating
class Event {
static hasMany = [ratings:Rating]
Double dRating
void setDRating(Double value){
this.dRating = value * 100/1000 // alternatively value /10
}
}
Do it in commandObject: If you don't want to put certain calculations and validation in domain then put these in command objects. See this. You can at any point of time assign values from command object to domain object by .properties binding mechanism. For example,
yourDomainObject.properties = yourcommandObjectObject.properties
Remember properties having same name would be binded.
Do it in service: You can do your calculations in service method, inject that service into your controller and call that method to perform calculations and even to persist to db as well. Remember services are by default transactional.
Hope it helps!

Lazy Eager loading Grails GORM

Ok i'm wondering what is the best way to implement my scenario.
My objects are as follows:
ProjectCategory has many Projects. Projects have a ProjectStatus. ProjectStatus has a property called name and name can be either "Closed" or "Open" etc...
I'm trying to display on a page all categories and the number of opened projects for that category next to the category name.
How would I go about doing that. The problem I'm seeing is that (using grails gorm) is that by default you cannot do something like
category.findAll{ it.status.name == "Opened" }.size()
because the objects are not loaded that deep. Now If I forced them to load, now for all categories I'm potentially loading a bunch of projects just to get the status. Wouldn't the system take a huge hit in performance the higher amount of projects you have?
The thought of creating a counter in the category and updating it every time a project status changes makes me cringe.
I must just be sleep deprived because I can't see what the proper way of doing this would be. If the way I mentioned first with the .findAll is the way to go, do I really have to worry about performance? How would I go about implementing that?
Thanks in advance for all your help.
I would use HQL. Assuming Projects belong to a ProjectCategory, you could add something like this to your ProjectCategory class:
class ProjectCategory {
// Fields/Methods
def getOpenedProjectsCount() {
ProjectCategory.executeQuery("SELECT count(*) FROM Projects p WHERE p.projectCategory = :projectCategory AND p.projectStatus.name = 'Opened'", [projectCategory: this])
}
}
Then when you have a ProjectCategory instance you can use the openedProjectsCount property:
def projectCategory = ProjectCategory.get(123)
projectCategory.openedProjectsCount

Removing multiple items from a Grails One to Many relationship

I'm creating an application using Grails 2.2.4 and Java 7 (these are constraints I cannot change) and I run into an odd behavior when trying to delete multiple entries in a Many-To-Many hasMany Set.
I have a class named Sport that contains the following:
Class Sport{
String name
static hasMany=[category:Category]
static belongsTo = [Category]
}
And another one named Category:
Class Category{
String name
static hasMany=[sports:Sport]
}
Now when in my CategoryController I try to delete multiple Sport instances from sports, my code compiles and runs without errors, but for reason only one of the selected instances is actually deleted.
If I get a Sport list and a Category id from a form and try to run the following code on every objet in the list:
def categoryInstance = Category.get(idCategory)
def sportInstance = Sport.get(idSport)
if(sportInstance!=null){
categoryInstance.removeFromSports(sportInstance)
}
categoryInstance.save()
Only the last instance is deleted.
If I run
def categoryInstance = Category.get(idCategory)
def sportInstance = Sport.get(idSport)
if(sportInstance!=null){
categoryInstance.removeFromSports(sportInstance)
categoryInstance.save()
}
Only the first one is deleted.
Note that this code is run from within a for loop over the params.sport.toList() list.
My guess is that this is either due to the fact that my sports Set is somehow "changed" after the first deletion and therefore Hibernate can't find the next instance, or that my save method commits the first change then "forgets" the next.
Any advice on how I can delete more than one instance at a time?
A workaround is to wrap the code for deleting a single association in a block of withNewSession + withNewTransaction
Sport.withNewSession {
Sport.withNewTransaction {
//delete 1 association here
}
}
It's not very elegant but works. Beware of a potential performance impact, as this will likely have many database roundtrips.
I don't know why this problem occurs, and googling didn't help either.
Another solution = workaround is to explicitly map the relationship as SportCategory, which you can then delete like any other object with SportCategory.delete().

What's the best practice for connecting information of two entities?

It's a bit difficult for me to put my questions in words, but I'll try.
I have two entities. One is called Product the other Sale. Each product has field definining the number of available items of this product. Each sale has a field stating the number of sold items of one product. Product to Sale is a OneToMany-Relationship.
Now if I want to find out if a product has sold out, I need to create a function which substracts the total number of sold items from the initial availability number of the product. Something like function getAvailableQuantity().
My question is: Where do I place this method?
I am tempted to put it in the Product Repository - but since this method needs to access the Sales entity this is against Dependency Injection. However, placing it in the controller seems like an unnecessary detour as I am losing the possibility of calling {{ product.getAvailableQuantity }} in Twig templates.
What is the way to go?
Thanks for any help!
I would suggest subtracting the sold items right away from Product::available_items. This would resolve the issue right away, because Product::available_items would always reflect the number of available items.
Method no. 1
Subtract Sale::quantity from Product right away.
public function setProduct(ProductInterface $product)
{
$new_count = $product->getAvailableItems() - $this->getQuantity();
$product->setAvailableItems($new_count);
$this->product = $product;
}
You'd also have to do the subtracting in all the controllers that might change the available item count.
Method no. 2
Personally, I would actually favor this method over the first one, because it keeps the logic in a single place and generally results in shorter controllers. In addition, my entities are usually long enough without all the domain logic.
Assuming Sale has a reference to a Product, I'd put the logic into a service (an entity-specific entitymanager, e.g UserManager. It'd be called SaleManager in our case). The method in it would look something similar to:
public function updateSale(SaleInterface $sale, $flush = true)
{
$product = $sale->getProduct();
$new_count = $product->getAvailableItems() - $sale->getQuantity();
if ($new_count < 0) {
throw new NegativeQuantityException();
}
$product->setAvailableItems($new_count);
$this->em->persist($sale);
if (true === $flush) {
$this->em->flush();
}
}
I didn't touch the issue of what would happen if you were to remove a Sale from a Product or switch a Product in a sale, but you must certainly consider this as well.
Where do I place this method?
Consider creating a ProductSalesManager service into which you inject the entity manager. From a controller you would have something like:
$productSalesManager = $this->get(product_sales.manager);
$products = $productSalesManager->getProductsWithAvailableQuantity();
Now you can put your logic in the manager without worrying about which repository it happens to be in. It also decouples your controller from Doctrine itself.
And I'm not so sure maintaining current inventory in the product itself is such a good idea but that is really up to you.
As the available quantity bellows to the Product, I think the work has to be done by the Product Entity.
If you use Doctrine, it has generated the function addSale in your Product Entity ( the name depend on how you define the relation). I should change this function to make the calculation.
Assuming the field in your product entity is named available_quantity and the field in your Sale Entity is named items_number:
public function addSale ( $sale )
{
$this->setAvailableQuantity($this->getAvailableQuantity() - $sale->getItemsNumber());
$this->sales[] = $sale;
}
Then in your controller you add the sale to the product:
// get the product
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
// build your Sale object
$sale = new Sale();
...
// add it to the product
$product->addSale($sale);
// save all
$em = $this->getDoctrine()->getEntityManager();
$em->persist($sale);
$em->persist($product);
$em->flush();
In your template you can get the available quantity of your product:
{{ product.availableQuantity }}

Editing a big imported file on a second page

This is mostly theoretical question, since I actually can implement it in any way, but it confuses me a bit. So, suppose I present a user with a page to select an Excel file, which is then uploaded to the server. Server code parses the file, and presents the user with another page with many options. The user can select and deselect some of them, edit names, and then click OK - after which the server has to process only the selected options.
The question may be:
is it better to store parsed file in Session?
is it better to push parsed data to client's page and then receive it back?
Here's example:
public class Data
{
public string Name { get; set; } // shown to user, can be changed
public bool Selected { get; set; } // this is in ViewModel but anyway
public string[] InternalData { get; set; } // not shown to user
}
// 1st option is to receive data via POST
public ActionResult ImportConfirmed(IList<Data> postitems)
{
// 2nd option is to receive only user changes via POST
var items = Session["items"] as IList<Data>;
items = items.Where(postitems of same name selected);
items.ForEach(set name to postitems name);
}
Obviously option #2 has less side effects, since it does not have global state. But in option #1 we don't push loads of useless-to-user data to the client. And this can be a lot.
Of course this problem is not new, and as always, the answer is: it depends.
I have to admit, I don't have any exact question in mind. I can't even tell why I don't like the Session solution which takes only couple of additional lines of code. The reason I ask is that I've read about Weblocks concept and was very impressed. So, I tried to invent something similar in ASP.NET MVC and failed to. Thus, I wonder, is there any elegant way to deal with such situations? By elegant I mean something that doesn't show it uses Session, easy to use, handles expirations (cleans up the Session if user does not press the final "Save" button), etc. Something like:
var data = parse(filestream);
var confirmationPostData = ShowView("Confirm", data);
items = items.Where(confirmationPostData of same name selected);
items.ForEach(set name to confirmationPostData name);
Here ShowView actually sends GET, wait for user's POST, and returns. Kind of. I do not insist, I just show the way that impressed me (in Weblocks - if I actually did understand it correctly).
Does everyone just use Session in such cases? Or is there a better way (except learning LISP which I already started to investigate if I can cope with)? Maybe, async actions in MVC v2 do it?
UPDATE: storing in DB/temp files, it works. I do sometimes store in DB. However this needs a way to expire the data since user may just abandon it (as simple as closing the browser). What I'm asking for: is there a proven and elegant way to solve it - not about how to do it. An abstraction built on top of serialization not tied to particular DB/file implementation, something like this.
I'm not sure what the purpose of uploading the Excel file is, but I like to make all actions that affect the long term state of the application, for the user, persisted. For example, what if the user uploads the file, changes a couple of options, then goes to lunch. If you store the info in session, it may be gone when they get back, ditto for storing it in the page with hidden variables. What about storing it in a DB?
I would store the file at the temp folder and only associate the name of the file with the user session so that later it can be processed:
// Create a temp file in the Temp folder and return its name:
var tempFile = Path.GetTempFileName();
// write to the temp file and put the filename into the session
// so that the next request can fetch the file and process it
There's a flaw with the GetTempFileName that I once fell into because I didn't read the documentation carefully. It says that the method will start throwing exceptions if you have more than 65535 files in the temp folder. So remember to always delete the temp file once you've finished processing it.
Another alternative to the temp folder would be to store the file into a database, but I am a little skeptic about storing files inside a relational database.

Resources