问题
I am creating backend based on Vapor 3.1.10 using Xcode 11.2 and Swift 5.1, database is PostgreSQL 12. I have a question: how to interact with database (CRUD) without POST and GET requests. All tutorials show how to CRUD only based on Request through HTTPS. But what if my app needs to save something in database without interacting with network? Look at my code:
import Vapor
import FluentPostgreSQL
final class Device: PostgreSQLModel {
var id: Int?
var isWorking: Bool
var serial: Int
init(isWorking: Bool, serial: Int) {
self.isWorking = isWorking
self.serial = serial
}
}
extension Device: Content {}
extension Device: Migration {}
extension Device: Parameter {}
classical method to write or read is:
import Vapor
final class DeviceController {
func readAll(_ req: Request) throws -> Future<[Device]> {
return Device.query(on: req).all()
}
func create(_ req: Request) throws -> Future<Device> {
return try req.content.decode(Device.self).flatMap { device in
return device.save(on: req)
}
}
}
How to replace req
to another background, safe thread, which I can create locally?
For example:
let device = Device(isWorking: true, serial: 54321)
device.save(on: <#T##DatabaseConnectable#>)
How to replace <#T##DatabaseConnectable#>
?
I will be thankful for any help or advice.
回答1:
Request
is Container
, it has eventLoop
and it is DatabaseConnectable
.
Application
, it is Container
, it has eventLoop
, but it is not DatabaseConnectable
.
How can you use Application
for database queries?
On any Container
you can get pooled connection to the database and this connection as you may guess is DatabaseConnectable
.
Example query in boot.swift
import Vapor
import FluentSQL
import FluentPostgreSQL
public func boot(_ app: Application) throws {
let _ = app.requestPooledConnection(to: .psql).flatMap { conn in
return User.query(conn).all().map { users in
print("just found \(users.count) users")
}.always {
try? app.releasePooledConnection(conn, to: .psql)
}
}
}
The code above will request pooled connection to PostgreSQL via .psql
identifier from Application
container, then execute query on that connection, then in always
block it release that connection back to pool.
Where to get a Container
for background task?
If you use https://github.com/vapor/jobs or https://github.com/MihaelIsaev/VaporCron you will have a Container
object in task declaration.
Example for VaporCron
// in boot.swift
import Vapor
import VaporCron
/// Called after your application has initialized.
public func boot(_ app: Application) throws {
scheduleTasks(on: app)
}
/// Scheduling Cron tasks
private func scheduleTasks(on app: Application) {
do {
_ = try VaporCron.schedule(Every1MinCheck.self, on: app)
} catch {
print("cron schedule error: \(error)")
}
}
// in Every1MinCheck.swift
import Vapor
import VaporCron
import FluentSQL
import PostgreSQL
struct Every1MinCheck: VaporCronSchedulable {
static var expression: String { return "*/1 * * * *" } // every 1 minute
static func task(on container: VaporCronContainer) -> Future<Void> {
return container.requestPooledConnection(to: .psql).flatMap { conn in
return User.query(conn).all().map { users in
print("just found \(users.count) users")
}.always {
try? container.releasePooledConnection(conn, to: .psql)
}
}
}
}
回答2:
Based on this question and answers (Is is possible to use Vapor 3 Postgres Fluent in a standalone script?) I realized CRUD like this:
import Vapor
import FluentPostgreSQL
final class Device: PostgreSQLModel {
var id: Int?
var isWorking: Bool
var serial: Int
init(isWorking: Bool, serial: Int) {
self.isWorking = isWorking
self.serial = serial
}
}
extension Device: Content {}
extension Device: Migration {}
extension Device: Parameter {}
final class WorkWithPostgres {
let databaseConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "username", database: "testestest", password: nil)
let database: PostgreSQLDatabase
static let shared = WorkWithPostgres()
private init() {
database = PostgreSQLDatabase(config: databaseConfig)
}
func readAll<T: PostgreSQLModel>(postgreSQLModel: T.Type, completion: (([T]) -> Void)?) {
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let conn = database.newConnection(on: worker)
let _ = conn.map { connection in
postgreSQLModel.query(on: connection).all().map { databaseData in
worker.shutdownGracefully { _ in
}
completion?(databaseData)
}
}
}
func create<T: PostgreSQLModel>(postgreSQLModel: T) {
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let conn = database.newConnection(on: worker)
let _ = conn.map { connection in
let _ = postgreSQLModel.save(on: connection).whenComplete {
worker.shutdownGracefully { _ in
}
}
}
}
}
final class DeviceController {
func readAll(completion: (([Device]) -> Void)?) {
WorkWithPostgres.shared.readAll(postgreSQLModel: Device.self) { devices in
completion?(devices)
}
}
func create(isWorking: Bool, serial: Int) {
let device = Device(isWorking: isWorking, serial: serial)
WorkWithPostgres.shared.create(postgreSQLModel: device)
}
}
It is working, but I am not sure is it good way to do this. Does somebody know?
来源:https://stackoverflow.com/questions/58755538/vapor-3-postgresql-crud-without-requests-http