Grails, MySQL and extended classes - mysql

Here is my abstract class :
package mypackage.commons
abstract class Content {
String name
static constraints = {
name nullable: false, blank: false, size: 1..50
}
}
Here is my class which is extended :
package mypackage.project
import mypackage.commons.Content
class Plane extends Content {
String something;
}
Here is my Bootstrap.groovy file :
package mypackage
import mypackage.commons.Content
import mypackage.project.Plane
class BootStrap {
def init = { servletContext ->
Plane boing1 = new Plane (name: 'Boing', something: 'Hello').save()
Plane boing2 = new Plane (name: 'Boing', something: 'Goodbye').save()
}
def destroy = {
}
}
The problem is when I go on MySQL, when I use SHOW TABLES, I only can see content plane.
Here is the content of SELECT * FROM content;
+----+---------+-------+-------------------------+-----------+
| id | version | name | class | something |
+----+---------+-------+-------------------------+-----------+
| 1 | 0 | Boing | mypackage.project.Plane | Hello |
| 2 | 0 | Boing | mypackage.project.Plane | Goodbye |
+----+---------+-------+-------------------------+-----------+
EDIT
After testing Mike's answer :
package mypackage.commons
abstract class Content {
String name
static constraints = {
name nullable: false, blank: false, size: 1..50
}
static mapping = {
tablePerHierarchy false
}
}
here is the result of SHOW TABLES
+-----------------------+
| Tables_in_my_database |
+-----------------------+
| content |
| plane |
+-----------------------+
Here is the result of SELECT * FROM content :
+----+---------+-------+
| id | version | name |
+----+---------+-------+
| 1 | 0 | Boing |
| 2 | 0 | Boing |
+----+---------+-------+
Here is the result of SELECT * FROM plane :
+----+------------+
| id | something |
+----+------------+
| 1 | Hello |
| 2 | Goodbye |
+----+------------+
END OF EDIT
Expected behaviour :
SHOW TABLES; should show me only the table plane
SELECT * FROM plane should show me this :
+----+---------+-------+------------+
| id | version | name | something |
+----+---------+-------+------------+
| 1 | 0 | Boing | Hello |
| 2 | 0 | Boing | Goodbye |
+----+---------+-------+------------+
How can I obtain the expected result ?
Is it possible ?
Thanks in advance.

You need to tell Grails to use table-per-subclass strategy by telling it not to use table-per-hierarchy like:
package mypackage.commons
abstract class Content {
String name
static constraints = {
name nullable: false, blank: false, size: 1..50
}
static mapping = {
tablePerHierarchy false
}
}

I found an answer, I wonder if it's the best way, but it works.
This topic helps me : https://github.com/grails/grails-data-mapping/issues/1254
Firstly, I understand that if I don't want a class to be mapped by Grails, I have to put this class outside of "Domain" package.
So I moove my Content class into src -> main -> groovy -> common (I created this folder) -> Content.groovy
Here is my Content class :
package common
abstract class Content {
String name
static constraints = {
name nullable: false, blank: false, size: 1..50
}
}
Here is my Plane class :
package mypackage.project
import common.Content
class Plane extends Content {
String something;
}
And here is my Bootstrap.groovy :
package mypackage
import mypackage.project.Plane
class BootStrap {
def init = { servletContext ->
Plane boing1 = new Plane (name: 'Boing', something: 'Hello').save()
Plane boing2 = new Plane (name: 'Boing', something: 'Goodbye').save()
}
def destroy = {
}
}
So I avoid tablePerHierarchy use.
In the topic, thay talked about #MappedSupperclass, I don't know what it refers to. I did not use it.
Expected behaviour is reach.
Is it the correct way to do it ?

Related

Laravel nested relations eager load respect ancestor id

Let's say I have a model called Research. Each research belongsToMany Location models. And each Location model BelongsToMany Contact models. BUT, each Contact is also related to Research.
class Research extends Model {
protected $table = 'researches';
public function locations()
{
return BelongsToMany( Location::class, 'research_locations_list', 'research_id', 'location_id' );
}
}
class Location extends Model {
protected $table = 'locations';
public function researches()
{
return BelongsToMany( Research::class, 'research_locations_list', 'research_id', 'location_id' );
}
public function contacts()
{
return BelongsToMany( Contact::class, 'location_contacts_list', 'location_id', 'contact_id' );
}
}
class Contact extends Model {
protected $table = 'contacts';
public function locations()
{
return BelongsToMany( Location::class, 'location_contacts_list', 'location_id', 'contact_id' );
}
}
researches table:
+----+------------+
| id | research |
+----+------------+
| 1 | Research 1 |
| 2 | Research 2 |
+----+------------+
locations table:
+----+---------------+
| id | location |
+----+---------------+
| 1 | United States |
| 2 | Great Britain |
| 3 | Germany |
+----+---------------+
contacts table:
+----+---------+
| id | contact |
+----+---------+
| 1 | Jack |
| 2 | John |
| 3 | Hanz |
+----+---------+
research_locations_list table:
+----+-------------+-------------+
| id | research_id | location_id |
+----+-------------+-------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
| 4 | 2 | 3 |
+----+-------------+-------------+
So Research 1 is being conducted in United States and Great Britain, Research 2 in Great Britain and Germany
location_contacts_list table:
+----+-------------+------------+-------------+
| id | location_id | contact_id | research_id |
+----+-------------+------------+-------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | 1 | 2 |
| 4 | 3 | 3 | 2 |
+----+-------------+------------+-------------+
Research 1 should have Jack and John as contacts in United States and no contacts elsewhere;
Research 2 should have John as contact in Great Britain and Hanz in Germany;
Now, with lazy load I can achieve that:
$researches = Research::all();
foreach( $researches as $research )
{
foreach( $research->locations as $location )
{
$contacts = $location->contacts()->wherePivot( 'research_id', $research->id )->get();
// Will return John and Jack in United States for Research 1 and John in Great Britain and Hanz in Germany for Research 2
}
}
Now, the question is: how do I achieve this with eager loading?
$researches = Research::with( 'locations.contacts' )->all();
foreach( $researches as $research )
{
foreach( $research->locations as $location )
{
$contacts = $location->contacts;
// Will return John and Jack in United States, John in Great Britain ( which is not supposed to happen ) for Research 1 and John in Great Britain and Hanz in Germany for Research 2
}
}
Perhaps I can instruct somehow for contacts to respect ancestor id? Like:
$research = Research::with( 'locations.contacts' )->where( 'researches.id = location_contacts_list.research_id' )->all();
UPDATE
The closest I came up to solving this is modifying the Location model like this:
class Location extends Model {
protected $table = 'locations';
public function researches()
{
return BelongsToMany( Research::class, 'research_locations_list', 'research_id', 'location_id' );
}
public function contacts()
{
return BelongsToMany( Contact::class, 'location_contacts_list', 'location_id', 'contact_id' );
}
// Modify contacts attribute getter
public function getContactsAttribute()
{
$contacts = $this->contacts();
if( !empty( $this->pivot->research_id ) )
{
$contacts = $contacts->wherePivot( 'research_id', $this->pivot->research_id );
}
return $contacts->get();
}
}
But it looks kind of dirty...
In your solution you get N+1 query problem. I can suggest the following solution:
class Research extends Model
{
protected $table = 'researches';
public function locations(): BelongsToMany
{
return $this->belongsToMany(Location::class, 'research_locations_list');
}
public function contacts(): BelongsToMany
{
return $this->belongsToMany(Contact::class, 'location_contacts_list')
->withPivot('location_id');
}
public function contactsByLocationAttribute(int $locationId): Collection
{
return $this->contacts
->filter(static function ($contact) use ($locationId) {
return $contact->pivot->location_id === $locationId;
});
}
}
$researches = Research::with(['locations', 'contacts'])->get();
foreach ($researches as $research) {
foreach ($research->locations as $location) {
$contacts = $research->contactsByLocation($location->id);
}
}
here there will always be only 3 queries to the database. And only necessary models will be loaded
If I got it right, you want to add some conditions inside your with statement. If you want to use eloquent syntax, you can do it like this:
$research = Research::with(['YOUR RELATION' => function ($query) {
$query->where('YOUR COLUMN', 'EQUALS TO SOMETHING');
}])->get();
Keep in mind that since inside with you use nested relationships, like locations.contacts, the where function inside the query, will filter only the last model (in this case that would be contacts). If you want to filter both locations and contacts based on some conditions, you have to write something similar to this (just an example):
$research = Research::with(['locations' => function ($query) {
$query->where('id', 1)->with(['contacts' => function ($query) {
$query->where('name', 'Tim');
}]);
})->get();
In order to do that though, you need to create a relationship also with your pivot table (if you want to use it also inside the conditions). Otherwise, you have to use a different syntax, using joins. Check this page from docs for query builders https://laravel.com/docs/9.x/queries#main-content
Perhaps, this https://laravel.com/docs/9.x/eloquent-relationships#has-many-through helps you. You should try to this

Laravel5: Eloquent and JOIN

Items Table
| id | item_id | item_title |
|-------|---------|------------|
| 1 | 1002 | A |
| 2 | 1003 | B |
| 3 | 1004 | C |
Sells Table
| id | item_id |
|----|-----------|
| 1 | 1002 1003 |
| 2 | 1003 1004 |
| 3 | 1004 1002 |
I want result : Sells Table 1. item title is A B
I want to combine the sells table with the item table and then match the item_id of the sells table to the item_title of the item table.
The table definitions look incorrect, you should have a pivot table linking items with sells, so a sell_item table:
item_id | sell_id
-----------------
1 | 1
1 | 3
2 | 1
2 | 2
3 | 2
3 | 3
Then using eloquent, you'd create models to represent your tables and define the relationships using BelongsToMany:
class Item extends Model {
public function sells() {
return $this->belongsToMany(Sell::class);
}
}
class Sell extends Model {
public function items() {
return $this->belongsToMany(Item::class);
}
}
Each instance of either model will then have access to it's related models via $item->sells and $sell->items.
The query builder can perform a join if not going the Eloquent route:
DB::table('sells')->join('items', 'sells.item_id', '=', 'items.item_id')
->select('sells.*', 'items.title')
->get();
The table definitions look incorrect, If you corrected already then your model replationship should be like
class Item extends Model {
public function sells() {
return $this->belongsToMany(Sell::class);
}
}
class Sell extends Model {
public function items() {
return $this->belongsToMany(Item::class);
}
}
Each instance of either model will then have access to it's related models via $item->sells and $sell->items.
The query builder can perform a join if not going the Eloquent route:
DB::table('sells')->join('items', 'sells.item_id', '=', 'items.item_id')
->select('sells.*', 'items.title')
->get();
Or if your model name is Sell then
$response=Sell::with('items')->get();

Deserialize JSON with nested categories

My Controller Class:
public function postAction(Request $request)
{
$content = $request->getContent();
$category = $this->get('jms_serializer')->deserialize($content,'AppBundle\Entity\Category','json');
$errors = $this->get('validator')->validate($category);
if (count($errors) > 0) {
return new View("NAME LENGTH MUST BE >4",Response::HTTP_BAD_REQUEST);
} else {
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
return new View($category, Response::HTTP_OK);
}
}
Entity:
class Category
{
private $id;
private $parent;
public function getChildren()
{
return $this->children;
}
private $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
//setters and getters
Doctrine.yml:
AppBundle\Entity\Category:
type: entity
oneToMany:
children:
targetEntity: AppBundle\Entity\Category
mappedBy: parent
orderBy:
name: ASC
manyToOne:
parent:
targetEntity: AppBundle\Entity\Category
inversedBy: children
joinColumn:
name: parentId
referencedColumn: id
table: category
repositoryClass: AppBundle\Repository\CategoryRepository
id:
id:
column: id
type: integer
id: true
generator:
strategy: AUTO
fields:
name:
type: string
lenght: 255
When I send POST json request like this:
{
"name": "Child to 8",
"parentId": "8"
}
In MySQL table i do not recieve parentId:
mysql> select * from category;
+----+--------------------+----------+
| id | name | parentId |
+----+--------------------+----------+
| 1 | Primary Category | NULL |
| 2 | Secondary Category | 1 |
| 3 | D_child | 1 |
| 4 | F_child | 1 |
| 5 | Z_child | 1 |
| 6 | Y_child | 1 |
| 7 | H_child | 1 |
| 8 | A_child | 1 |
| 9 | Child to 8 | NULL |<----- must be 8
+----+--------------------+----------+
But after deserialization i receive this:
{
"id": 9,
"name": "Child to 8"
}
I understand that id is an integer, but parentId is already an object of class Category. But how to make it so that he also signed up?
How can i do this? Maybe I do not understand something ...
You need to have a .yml config file for serializer. In your case - Entity.Category.yml.
In this file add property of nested entities, set him a type of you Entity and for be sure accessors (setter, getter).

How to create MultiLevel table in angularjs

I want to create table (not use ul li) as below
-----------------
# | name |
-----------------
1 | Level_1-a |
1 | Level_2.1 |
2 | Level_2.2 |
3 | Level_2.3 |
1 | Level_2.3.1|
2 | Level_2.3.2|
2 | Level_1-b |
.. | Level_.. |
n | Level_n |
..................
With items
$scope.items = [{
name: "level_1",
child: {
name: "level_2",
child: {
name: "level_3",
child: {
name: "level_...",
child: {
name : "level_n",
child : { ....}
}
}
}
}
}];
I dont have idea to do it. So please help me.
Thank all.
If possible, I would reformat the JSON. Doing so, you could do a simple ng-repeat in a table. Much cleaner code. If you do not reformat the JSON, I think you will need to do a ng-repeat at each level.

MDX result to JSON using C#

I'm new to MDX, basically I need to serialize the SSAS MDX result to a JSON object.
MDX Query:
SELECT
(
[Measures].[Max Available]
) ON COLUMNS
, NON EMPTY
(
[Application].[Product].Children * [Application].[Application Name].Children
) DIMENSION PROPERTIES MEMBER_CAPTION ON ROWS
FROM [Applications]
Let's say I have a MDX result that looks like:
__________________________________
| | | Measure1 |
| Product1 | Feature1 | 1 |
| Product1 | Feature2 | 1 |
| Product1 | Feature3 | 10 |
| Product2 | Feature1 | 1 |
| Product2 | Feature2 | 1 |
| Product3 | Feature1 | 1 |
| Product3 | Feature2 | 1 |
| Product3 | Feature3 | 1 |
| Product3 | Feature4 | 1 |
I need to create a JSON object that looks like (I don't need the Measurement values, I just used them to get valid list of products and features in the MDX Heirarchy):
[
{
"product":"Product1",
"feature":[
"Feature1",
"Feature2",
"Feature3"
]
}, {
"product":"Product2",
"feature":[
"Feature1",
"Feature2"
]
}, {
"product":"Product3",
"feature":[
"Feature1",
"Feature2",
"Feature3",
"Feature4"
]
}, {
...
}
]
I use ADOMD.NET library using ExecuteCellSet(), but I'm also new to this one as well. Can someone point me to the right direction?
Thanks,
dfox
Although I am not familiar with MDX, I have used JSON in C#. One way to create a JSON object (since they are not native in C# as of 4.5) is by using DataContracts.
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
namespace Interface.Data
{
[DataContract]
class jsonObject
{
[DataMember]
public String product="";
[DataMember]
public String[] feature={"", "", ""};
public jsonObject(){}
}
}
Then in your MDX query, assuming you are storing the query data in some string 'line', you can do:
using System.Runtime.Serialization.Json;
using System.Data.Common;
using System.IO;
static jsonObject json;
public static MDXObj parseMDXObj(String line)
{
MDXObj obj = new MDXObj(getMDX());
try
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(jsonObject));
MemoryStream stream = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(line));
json = (jsonObject)ser.ReadObject(stream);
}
catch (Exception e)
{
Console.WriteLine("Error Occurred in creating JSON: " + e.Message);
return null;
}
return obj;
}