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'm using fmdb library.
https://github.com/ccgus/fmdb
I would like to insert new records but I don't know how can I do:
if let db = database, let q = db.executeUpdate("insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable) values (\(catid),\(subcat_id),\(id),'\(subcat_title)','\(title)','\(description)','\(lat)','\(lon)','\(takhfif)','\(images)',\(wifi),\(apple_health),\(wc),\(full_time),\(pos),'\(work_hours)','\(phone)','\(mobile)','\(fax)','\(website)','\(email)','\(address)','\(facebook)','\(instagram)','\(linkedin)','\(telegram)','\(googleplus)','\(twitter)',\(publish),\(feature),'\(manager)','\(city)',\(rating_sum),\(rate_count),'\(lastip)',\(parking),\(isMallID),\(mallID),'\(discount_images)','\(price_images)','\(newProduct_images)','\(services_images)',\(order_online),\(out_upon),'\(cat_title)','\(cat_icon)',\(last_modify),'\(item_logo)','\(cat_logo)',\(rate_sum1),\(rate_sum2),\(rate_sum3),\(rate_count1),\(rate_count2),\(rate_count3),'\(rate_title1)','\(rate_title2)','\(rate_title3)',\(rate_enable))") {
}
but I got this message before building:
cannot invoke executeUpdate with an argument list of type string
You used the api wrong. The first parameter is the sql, each value has to be represented with an "?". The second one (values) is an array of the values. Try this:
if let db = database, let q = db.executeUpdate(
"insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable) values (?, ?, ? ...., ?, ?, ? )"
values: [catid, subcat_id, id, subcat_title, title, description, lat, lon, takhfif, images, wifi, apple_health, wc, full_time, pos, work_hours, phone, mobile, fax, website, email, address, facebook, instagram, linkedin, telegram, googleplus, twitter, publish, feature, manager, city, rating_sum, rate_count, lastip, parking, isMallID, mallID, discount_images, price_images, newProduct_images, services_images, order_online, out_upon, cat_title, cat_icon, last_modify, item_logo, cat_logo, rate_sum1, rate_sum2, rate_sum3, rate_count1, rate_count2, rate_count3, rate_title1, rate_title2, rate_title3, rate_enable]) {
}
Note: I didn't count the values, so you have to ensure that the count of "?" inside of the string is exactly the count of your values!
Maybe you should think about a function to improve the readability of your code. Something like that:
extension FMDatabase {
func insert(into table: String, row values: [(column: String, content: Any)]) {
guard (values.count > 0) else { fatalError(values.count > 0, "Inserting nothing into a table makes no sense!") }
var sql = "INSERT INTO \(table) ("
var contents: [Any] = []
for (index, value) in values.enumerated() {
sql += index == 0 ? value.column : ", \(value.column)"
contents.append(value.content)
}
sql += ") VALUES ("
for index in 0..<values.count {
sql += index == 0 ? "?" : ", ?"
}
sql += ")"
executeUpdate(sql, values: contents)
}
}
Then you can insert values like that:
if let db = database {
db.insert(
into: table,
row: [
(column: "catid", content: catid),
(column: "subcat_id", content: subcat_id),
...
(column: "rate_enable", content: rate_enable)
]
)
}
Isn't it better readable?
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.