I'm using the plugin Platform Core and the module of Event Bus. I need to add some domains assync, but i force sync to test, in my database but de exception DataIntegrityViolation is throwed everytime i tried to save(). That is the domain structure:
class Alocacao {
static belongsTo = [tipoUnidade: TipoUnidade]
//Anothers attrs and constraints
}
class TipoUnidade {
static belongsTo = [ estabelecimento : Estabelecimento];
static hasMany = [alocacoes: Alocacao];
List<Alocacao> alocacoes
//Constraints and fields...
}
In my controller i have this code:
tipoUnidadeService.save(tipoUnidadeInstance)
event('criarAlocacaoQuartoEvent', tipoUnidadeInstance, [fork: false])
And in my event listener this:
class AlocacaoService {
#Listener(topic="criarAlocacaoQuartoEvent", namespace="app")
def defaultAlocacaoCreator(EventMessage<TipoUnidade> message) {
Canal canalSist = Canal.findByNomeIlike("%manual%")
TipoUnidade quarto = TipoUnidade.get(message.data.id)
def alocacaoSistema = new Alocacao(exclusivo: 0, nExclusivo: 0, data: tmp.toDate(), canal: canalSist, tipoUnidade: quarto).save(failOnError: true) //EXCEPTION!!
}
}
I don't know what's happen with that code.
This is the entire exception:
util.JDBCExceptionReporter SQL Error: 1048, SQLState: 23000
util.JDBCExceptionReporter Column 'tipo_unidade_id' cannot be null
services.AlocacaoService could not insert: [br.com.qreserva.portal.domains.Alocacao]; SQL [insert into alocacao (version, canal_id, data, date_created, exclusivo, n_exclusivo, tipo_unidade_id, alocacoes_idx) values (?, ?, ?, ?, ?, ?, ?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]
org.springframework.dao.DataIntegrityViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]; SQL [insert into alocacao (version, canal_id, data, date_created, exclusivo, n_exclusivo, tipo_unidade_id, alocacoes_idx) values (?, ?, ?, ?, ?, ?, ?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]
at br.com.qreserva.portal.services.AlocacaoService.defaultAlocacaoCreator(AlocacaoService.groovy:178)
at org.grails.plugin.platform.events.registry.DefaultEventsRegistry$ListenerHandler.invoke(DefaultEventsRegistry.java:238)
at org.grails.plugin.platform.events.registry.DefaultEventsRegistry.invokeListeners(DefaultEventsRegistry.java:160)
at org.grails.plugin.platform.events.publisher.DefaultEventsPublisher.event(DefaultEventsPublisher.java:79)
at org.grails.plugin.platform.events.EventsImpl.event(EventsImpl.groovy:154)
at org.grails.plugin.platform.events.EventsImpl$_closure1_closure5_closure9.doCall(EventsImpl.groovy:83)
at br.com.qreserva.portal.controllers.TipoUnidadeController.salvar(TipoUnidadeController.groovy:126)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]
... 10 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'tipo_unidade_id' cannot be null
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922)
at com.jolbox.bonecp.PreparedStatementHandle.executeUpdate(PreparedStatementHandle.java:203)
... 10 more
[EDIT]
I create this git project to illustrate my problem and the same errors occurs
[SOLUTION]
I solved this using the addTo* attrs in belongsTo class. But why this works i have no idea.
The core problem is
MySQLIntegrityConstraintViolationException: Column 'tipo_unidade_id' cannot be null
That column is the foreign key for the tipoUnidade variable (the camel-case tipoUnidade becomes tipo_unidade as a column name, and has the _id suffix added because it's a FK) so that implies that the value there is null, meaning that TipoUnidade.get(message.data.id) returned null. This will happen if message.data.id is null (possibly because message.data is not null but not saved yet?), or if there's no record for that id.
Since you're working with async code, my guess is that it's a timing issue, and the message.data value isn't saved yet, but you're trying to load a value based on it having been inserted.
Check the values, and consider turning on SQL logging so you can see the insertion and retrieval order for the items.
I have the same issue with EventBus. The problem is that EventBus works with threads. I suppose you use Transactions too. The both objects were created in two different threads and hibernate can't can't save one of them because the other is not available.
Based on response of Burt i make a workaround and for some reason using the addTo* resolve the problem.
TipoUnidade quarto = TipoUnidade.get(message.data.id)
quarto.addToAlocacoes(new Alocacao(exclusivo: 0, nExclusivo: 0, data: tmp.toDate(), canal: canalSist))
quarto.save(failOnError: true)
Thx.
Related
Is there a way to generate multiple row sql query (values only) using some functional constructs of array?
I've an array of Roles that I want to insert into sqlite database.
struct Role {
var id: Int32
var name: String?
}
func updateByUserId(_ id: Int32, _ roles: [Role]) {
let sql = "INSERT INTO user_role(user_id, role_id) VALUES( \(id), \(roles.map..) )"
}
Expectation:
for instances if id is 1 and roles has an array [10, 11, 14, 15]
Generated SQL should be
INSERT INTO user_role(user_id, role_id) VALUES(1, 10), (1, 11), (1, 14), (1, 15)
SQL Syntax for Multiple row insert is
INSERT INTO MyTable ( Column1, Column2 ) VALUES(Value1, Value2),
(Value1, Value2)
You can map each role to the string (id, role), then join the array of strings with the separator ,:
let values = roles.map { "(\(id), \($0))" }.joined(separator: ", ")
let sql = "INSERT INTO user_role(user_id, role_id) VALUES\(values)"
Although for this particular scenario the SQL string computation is not problematic, it's good practice to use parametrized statements for every DB query.
Working exclusively with parametrized statements avoids vulnerabilities like SQL injection, or malformed queries that fail to execute (when dealing with strings instead of ints).
So, I'd recommend going via the above route by writing something like this:
func updateByUserId(_ id: Int32, _ roles: [Role]) -> (statement: String, params: [Int32]) {
let statement = "INSERT INTO user_role(user_id, role_id) VALUES " + Array(repeating: "(?, ?)", count: roles.count).joined(separator: ", ")
let params = roles.flatMap { [id, $0.id] }
return (statement, params)
}
For your example in the question, the output would be something like this:
(statement: "INSERT INTO user_role(user_id, role_id) VALUES (?, ?), (?, ?), (?, ?), (?, ?)", params: [1, 10, 1, 11, 1, 14, 1, 15])
You can then use the SQLite functions to create the parametrized statement and bind the given values to it.
P.S. There is also the matter of validating that the array of roles is not empty, in which case you'd get an invalid SQL as output. To handle this, you can make the function return an optional, and nil will signal an empty array. Doing this will enable a small performance improvement, as you'll be able to use String(repeating:count:), which is a little bit faster than creating an array and joing it later on:
func updateByUserId(_ id: Int32, _ roles: [Role]) -> (statement: String, params: [Int32])? {
guard !roles.isEmpty else { return nil }
return (statement: "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)" + String(repeating: ", (?, ?)", count: roles.count - 1),
params: roles.flatMap { [id, $0.id] })
}
I'm trying to execute raw query in typeorm with parameters
I tried following queries:
insert into data(id, name, gender) values(?, ?,?)
insert into data(id, name, gender) values($1, $2, $3)
insert into data(id, name, gender) values(:id, :name, :gender)
The typeorm code is:
import { getManager } from 'typeorm';
await getManager().query(query, [1, 'test', 'male']);
What is wrong? Is there any other way?
Issue solved with this link. It depends on the underlying database which syntax to use.
https://github.com/typeorm/typeorm/issues/881
as #ashutosh said, it depends on the driver of your database
For mysql/mysql2 you should use ? as placehoder. For example
manager.query('SELECT id FROM foos WHERE createdAt > ? AND id > ?', [new Date(), 3])
For oracle, you can use something like this for me like this :
const querySingleValue = SELECT * FROM TABLE1 WHERE name in (:param) ;
string value :
getManager().query(querySingleValue,[param]) ;
I am using the table per subclass strategy in Grails by setting the tablePerHierarchy property of the static mapping field in my superclass to false. This way, Grails creates one table for my superclass and one additional table for each of my subclasses.
However, while the superclass and subclass records share the same ID (primary key), there are no foreign key constraints to keep them consistent, i.e. it is possible to delete the superclass record, leaving the subclass record in an invalid state. I want to know if there is a setting/property to make GORM address this in some way, e.g. through constraints. Or is my only option to add foreign keys manually?
For example, given the following domain class as superclass:
class Product {
String productCode
static mapping = {
tablePerHierarchy false
}
}
And the following domain class as subclass:
class Book extends Product {
String isbn
}
This results in the creation of two tables, the Product table and the Book table. When creating a Book – through scaffolded pages, for instance – a record is inserted into each table, their only link being the fact that the ID value is the same for each. Specifically, the data might look like this:
PRODUCT
Id Version ProductCode
1 1 BLAH-02X1
BOOK
Id ISBN
1 123-4-56-7891011-1
Because there is no formal relationship defined at the database level for these tables, it is possible to delete one of the records and leave the other, which results in invalid data. Obviously I can use SQL to manually create a foreign key constraint on the two ID fields, but I was hoping to let Grails handle that. Is this possible?
Using Grails 2.2.1
Solved!
The following solution fixed this issue for me. Add the class below to src/java (this class cannot be written in Groovy)
package org.example;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import java.util.Iterator;
public class TablePerSubclassConfiguration extends GrailsAnnotationConfiguration {
private static final long serialVersionUID = 1;
private boolean alreadyProcessed = false;
#Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if (alreadyProcessed) {
return;
}
for (PersistentClass persistentClass : classes.values()) {
if (persistentClass instanceof RootClass) {
RootClass rootClass = (RootClass) persistentClass;
if (rootClass.hasSubclasses()) {
Iterator subclasses = rootClass.getSubclassIterator();
while (subclasses.hasNext()) {
Object subclass = subclasses.next();
// This test ensures that foreign keys will only be created for subclasses that are
// mapped using "table per subclass"
if (subclass instanceof JoinedSubclass) {
JoinedSubclass joinedSubclass = (JoinedSubclass) subclass;
joinedSubclass.createForeignKey();
}
}
}
}
}
alreadyProcessed = true;
}
}
Then in DataSource.groovy set this as the configuration class
dataSource {
configClass = 'org.example.TablePerSubclassConfiguration'
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
Update
I've submitted a pull request to Grails for this issue. The fix was included in Grails 2.3.8 or 2.3.9 (can't remember which).
Hibernate ensures the data integrity in case of table per subclass. In case of table per subclass, subclass maintains a primary key association with superclass. Have a look at Hibernate Table per subclass. To validate the fact here is your test case:
class Product {
String productCode
static mapping = {
tablePerHierarchy false
}
}
class Book extends Product{
String isbn
}
//Test Case
def testTablePerSubclass{
def product = new Product(productCode: 'XYZ456')
product.save(flush: true, failOnError: true)
def book = new Book(isbn: '123456123', productCode: 'ABC123')
book.save(flush: true, failOnError: true)
assert Book.list().size() == 1 //One Book
assert Book.list()*.id == [2] //Book id
assert Product.list().size() == 2 //One Product, one Book (2 Products)
assert Product.list()*.id == [1, 2] //Product id, Book Id
//Grab the product (book) to delete
def productToDelete = Product.get(book.id)
productToDelete.delete(flush: true)
assert Book.list().isEmpty() //Book deleted from Book table as well
assert Product.list().size() == 1 //One Product remaining in Product table
assert Product.list()*.id == [1] //Remaining Product Id
}
Keep logSql true in DataSource.groovy to see corresponding sqls getting executed.
Log Sql Output:-
Hibernate: insert into product (id, version, product_code) values (null, ?, ?)
Hibernate: insert into product (id, version, product_code) values (null, ?, ?)
Hibernate: insert into book (isbn, id) values (?, ?)
Hibernate: select this_.id as id0_0_, this_1_.version as version0_0_, this_1_.product_code as product3_0_0_, this_.isbn as isbn1_0_ from book this_ inner join product this_1_ on this_.id=this_1_.id
[com.example.Book : 2]
Hibernate: select this_.id as id0_0_, this_.version as version0_0_, this_.product_code as product3_0_0_, this_1_.isbn as isbn1_0_, case when this_1_.id is not null then 1 when this_.id is not null then 0 end as clazz_0_ from product this_ left outer join book this_1_ on this_.id=this_1_.id
[com.example.Product : 1, com.example.Book : 2]
Hibernate: delete from book where id=?
Hibernate: delete from product where id=? and version=?
Hibernate: select this_.id as id0_0_, this_1_.version as version0_0_, this_1_.product_code as product3_0_0_, this_.isbn as isbn1_0_ from book this_ inner join product this_1_ on this_.id=this_1_.id
[]
Hibernate: select this_.id as id0_0_, this_.version as version0_0_, this_.product_code as product3_0_0_, this_1_.isbn as isbn1_0_, case when this_1_.id is not null then 1 when this_.id is not null then 0 end as clazz_0_ from product this_ left outer join book this_1_ on this_.id=this_1_.id
[com.example.Product : 1]
Using Grails 2.2.2
I need to call a stored procedure when the Insert Script trigger is called.
The idea is use the stored procedure to insert rows in 3 tables, including the original table.
I have the following tables
- cars
- makers
- models
the stored procedure receives the car's data, maker's name and model's name. The SP looks in the makers and models table and insert a new record if not found.
This is my script trigger code:
function insert(item, user, request) {
var params = [item.name, item.year, item.maker_name, item.model];
var sql = "exec myCars.sp_insert_car ?, ?, ?, ?";
mssql.query(sql, params,
{
success: function(results) {
request.respond(statusCodes.OK,{
results: results,
count: 1
});
}
});
}
Because the sp_insert_car inserts a row in the cars table, the trigger is called again. The weird thing is that it is also called when a new maker or model is inserted.
Is there a way to disable the second time the trigger is called?
Also, the SP has an output parameter that returns the new car id. Is there a way to get this output parameter?
I appreciate any help you can provide
I think you want to run the request.execute first to get the data into the original table. Then in the success function run your stored procedure to add the data, if missing, to the other tables. Something along the lines of...
function insert(item, user, request) {
request.execute({
success: function() {
var params = [item.name, item.year, item.maker_name, item.model];
var sql = "myCars.sp_insert_car ?, ?, ?, ?";
mssql.query(sql, params, {
success: function(results) {
request.respond(statusCodes.OK,{
results: results,
count: 1
});
}
});
}
});
}
This also lets you grab the id of the newly inserted row in the original table, item.id, for use as a passed-in parameter for the stored procedure if you need it.
add this line ...
var mssql=request.service.mssql;
above underline
mssql.query ("EXEC dbo.Sp_User_Authentication ?, ?",request.query.username,request.query.password)
class Pirate {
String name
static belongsTo = Ship
static hasMany = [ships: Ship]
}
class Ship {
String name
static hasMany = [crew: Pirate]
}
new Ship(name: "Ship1").addToCrew(new Pirate(name: "pirate1")).save()
The above code is only saving an entry in the Pirate and Ship table but not in the ship_crew table.
I'm aware of this technique used by Burt in the Spring Security Grails plugin but I'd still like to be able to use the hasMany and belongsTo
UPDATE
The above line of code seems to be saving the entries in all three: Pirate, Ship, and ship_crew table, however, from the console it only saves entries in the two tables and not in ship_crew
> new Ship(name: "Ship10").addToCrew(new Pirate(name: "pirate10")).save(failOnError:true)
new Ship(name: "Ship10").addToCrew(new Pirate(name: "pirate10")).save(failOnError:true)
Hibernate:
/* insert playwithgrails.Ship
*/ insert
into
ship
(version, name)
values
(?, ?)
Hibernate:
/* insert playwithgrails.Pirate
*/ insert
into
pirate
(version, name)
values
(?, ?)
===> playwithgrails.Ship : 29