外观
sql
原文: https://finproducts.atlassian.net/wiki/spaces/JIDD3/pages/34793947262/FIN+5.3.1+sql 中文整理基于 FIN 5.3.1
导读: 关系数据库访问
以下为 API 参考,保留英文以便对照源码;目录已列出(英文标题便于检索);概述已尽量中译。
目录(导读)
- mixins
- classes
- errs
- docs
- Overview
- Connections
- Connections in Java
- Statements
- Escape Sequences
- Queries
- sql::Row
- SQL/Fantom Type Mapping
- Test Setup
- Slot Details
- index
- make
- name
- sqlType
- toStr
- type
- col
- cols
- get
- set
- trap
- autoCommit
- close
- commit
- isClosed
- meta
- open
- rollback
- sql
- withPrepare
- checkLinger
- execute
- linger
- log
- maxConns
- onClose
- onOpen
- password
- timeout
- uri
- username
- driverName
- driverVersion
- driverVersionStr
- maxColName
- maxTableName
- productName
- productVersion
- productVersionStr
- tableExists
- tableRow
- tables
- executeBatch
- limit
- prepare
- query
- queryEach
- queryEachWhile
API 正文(英文)
errs
| SqlErr | SqlErr indicates an error from the SQL database driver. |
docs
Overview
The sql pod provides a standardized, low level API to work with relational databases. Its basic goals in life:
- Connections: manage interaction with the database
- Statements: execute SQL statements
- Model: model meta-data and relational tables
Connections
Connections are managed by the SqlConn class. To open and close connections to the database, simply call the SqlConn.open and SqlConn.close methods.
A pool of Connections is managed by the SqlConnPool class. To use a connection created by this pool, use the SqlConnPool.execute method.
Connections in Java
When running in a Java VM, Fantom uses JDBC under the covers. Using MySQL as an example, follow these steps to open a connection in the JVM:
Ensure your JDBC driver is installed and available via the system class path. The best place to stick it is in the "jre/lib/ext" directory. You can use
fan -versionto locate your JRE directory. The driver is packaged up as something like "mysql-connector-j-9.0.0.jar" or "postgresql-42.7.3.jar".Ensure the JDBC class is loaded into memory. The simplest way to preload the class is to ensure the classname is defined in "etc/sql/config.props" :
java.drivers=java.drivers=com.mysql.cj.jdbc.Driver,org.postgresql.DriverOpen a SqlConn instance using the JDBC URL:
db := SqlConn.open("jdbc:mysql://localhost:3306/fantest", "fantest", "fantest")
If using Microsoft SQL Server:
- Assuming you are running Java 1.6 or higher, then make sure you put only "sqljdbc4.jar" into your classpath (do not put "sqljdbc.jar" in the path)
- Classname is "com.microsoft.sqlserver.jdbc.SQLServerDriver" (for java.drivers in etc/sql/config.props)
- JDBC URL format is "jdbc:sqlserver://{host};database={name}"
Statements
SQL statements are created using the SqlConn.sql method. After a statement has been created, it can be executed immediately by calling Statement.execute or it can be prepared for later execution by calling Statement.prepare.
For example, to create a table in MySQL:
db.sql("create table Books (
id integer auto_increment not null,
title varchar(128) not null,
author varchar(128) not null,
year integer,
primary key (id))").executePrepared statements can be parameterized by including named parameters in the SQL text. For example:
addBook := db.sql("insert into Books (title, author, year)
values (@title, @author, @year)").prepareThis statement can then be executed multiple times with different parameters.
addBook.execute(["title":"David Copperfield", "author":"Charles Dickens", "year":1850])
addBook.execute(["title":"Hard Times", "author":"Charles Dickens", "year":1854])
addBook.execute(["title":"The Jungle Book", "author":"Rudyard Kipling", "year":1894])
addBook.execute(["title":"Captains Courageous", "author":"Rudyard Kipling", "year":1897])Escape Sequences
When calling Statement.prepare, the strings \@ and \\ in the Statement's SQL are escaped into @ and \ respectively.
Examples:
In mysql, to refer to the user variable @v1 in a prepared Statement:
select name, \@v1 from foo where id = @id;In postgres, to use the JSONB operator @> in a prepared Statement:
select * from foo where data \@> '{"x": 99}'::jsonb;There is an old, undocumented syntax for referring to mysql user variables, using the escape sequence @@v1, for example:
select name, @@v1 from foo where id = @id;This syntax is deprecated and is no longer supported by default. If you have existing code that uses this undocumented feature, you must add the following entry to "etc/sql/config.props":
deprecatedEscape=trueQueries
The result of an SQL query is always a relational table described by fixed columns with zero or more rows. Fantom supports two different mechanisms for retrieving query results: as a list of Rows or by iterating the rows with a closure.
To fetch as a list of rows:
stmt := db.sql("select title, year from Books
where author = @author and year > @year").prepare
dickensNovels := stmt.query(["author":"Charles Dickens", "year":1850])
kiplingNovels := stmt.query(["author":"Rudyard Kipling", "year":1890])Or to iterate through the rows:
lastPublished := 0
stmt.queryEach(["author":"Charles Dickens", "year":0]) |Row row|
{
lastPublished = lastPublished.max(row->year)
}sql::Row
The rows for a given query result all share a list of sql::Cols which describes the meta-data. You can use access the column of each row using the get(Col) or with dynamic invoke:
// using dynamic invoke
dickensNovels.each |Row book| { echo("${book->title}, ${book->year}") }
// using col
title := row.col("title")
year := row.col("year")
dickensNovels.each |Row book| { echo("${book[title]}, ${book[year]}") }If you have a large query result, using get(Col) provides a little better performance.
SQL/Fantom Type Mapping
The following type specifies the mapping of SQL types to Fantom types:
SQL Type Fantom Type
-------- -----------
CHAR sys::Str
VARCHAR sys::Str
LONGVARCHAR sys::Str
TEXT[] sys::List of sys::Str
BIT sys::Bool
TINYINT sys::Int
SMALLINT sys::Int
INTEGER sys::Int
BIGINT sys::Int
REAL sys::Float
FLOAT sys::Float
DOUBLE sys::Float
DECIMAL sys::Decimal
NUMERIC sys::Decimal
BINARY sys::Buf
VARBINARY sys::Buf
LONGVARBINARY sys::Buf
TIMESTAMP sys::DateTime
DATE sys::Date
TIME sys::Time
BYTEA sys::Buf
catch-all sys::StrTest Setup
The unit test sql::SqlTest runs automatically against both mysql and postgres. To run this test, both of these DBMS systems must be installed locally and running on the default port, and they each must have the following one-time configuration applied.
For msyql, setup the fantest database and user account via:
mysql -u root -p
mysql> create user fantest identified by 'fantest';
mysql> create database fantest;
mysql> grant all privileges on fantest.* to fantest;For postgres, setup the fantest database and user account via:
psql -U postgres
postgres=# create role fantest with login superuser password 'fantest';
postgres=# alter role fantest with login;
postgres=# create schema authorization fantest;In addition, both JDBC drivers must be installed, and "etc/sql/config.props" must have a reference to the classpath of both drivers, e.g.
java.drivers=java.drivers=com.mysql.cj.jdbc.Driver,org.postgresql.DriverCol
const class Col : Obj
Col models a column of a relational table. Columns are accessed from rows with Row.col and Row.cols
Constructors
| make | Construct a column for the given meta-data. |
Fields
| index | Zero based index of the column in the query result. |
| name | Name of the column. |
| sqlType | The type of the column as defined by the SQL database. |
| type | Type of the column. |
Methods
| toStr | Return name. |
Slot Details
index
const Int index
Zero based index of the column in the query result.
make
new make(Int index, Str name, Type type, Str sqlType)
Construct a column for the given meta-data.
name
const Str name
Name of the column.
sqlType
const Str sqlType
The type of the column as defined by the SQL database. See type mapping.
toStr
virtual override Str toStr()
Return name.
type
const Type type
Type of the column. See type mapping.
Row
class Row : Obj
Row models a row of a relational table. See pod-doc.
Methods
| col | Get a column by name. |
| cols | Get a read-only list of the columns. |
| get | Get column value. |
| set | Set a column value. |
| toStr | Dump the cells separated by a comma. |
| trap | Trap is used to get or set a column by name. |
Slot Details
col
Col? col(Str name, Bool checked := true)
Get a column by name. If not found and checked is true then throw ArgErr, otherwise return null.
cols
Col[] cols()
Get a read-only list of the columns.
get
@Operator Obj? get(Col col)
Get column value. See type mapping.
set
@Operator Void set(Col col, Obj? val)
Set a column value. See type mapping.
toStr
virtual override Str toStr()
Dump the cells separated by a comma.
trap
virtual override Obj? trap(Str name, Obj?[]? args := null)
Trap is used to get or set a column by name.
SqlConn
mixin SqlConn
SqlConn manages a connection to a relational database. See pod-doc.
Fields
| autoCommit | If auto-commit is true then each statement is executed and committed as an individual transaction. |
Methods
| close | Close the database connection. |
| commit | Commit all the changes made inside the current transaction. |
| isClosed | Return if close has been called. |
| meta | Get the database meta-data |
| open | Open a connection to the database specified by the given JDBC uri and username/password credentials. |
| rollback | Undo any changes made inside the current transaction. |
| sql | Create a statement for this database. |
| withPrepare | withPrepare creates a prepared Statement, passes the Statement to the callback, and then closes the prepared Statement. |
Slot Details
autoCommit
abstract Bool autoCommit
If auto-commit is true then each statement is executed and committed as an individual transaction. Otherwise statements are grouped into transaction which must be closed via commit or rollback.
close
abstract Bool close()
Close the database connection. Closing a connection already closed is a no-op. This method is guaranteed to never throw an exception. Return true if the connection was closed successfully or false if closed abnormally.
Do not close connections that were created by SqlConnPool. The pool handles that for you.
commit
abstract Void commit()
Commit all the changes made inside the current transaction.
isClosed
abstract Bool isClosed()
Return if close has been called.
meta
abstract SqlMeta meta()
Get the database meta-data
open
static SqlConn open(Str uri, Str? username, Str? password)
Open a connection to the database specified by the given JDBC uri and username/password credentials. Raise exception if connection cannot be established. See pod-doc.
rollback
abstract Void rollback()
Undo any changes made inside the current transaction.
sql
abstract Statement sql(Str sql)
Create a statement for this database.
withPrepare
Obj? withPrepare(Str sql, |Statement->Obj?| f)
withPrepare creates a prepared Statement, passes the Statement to the callback, and then closes the prepared Statement.
The method returns the value returned by the callback.
SqlConnPool
const class SqlConnPool : Obj
SqlConnPool manages a pool of reusable SQL connections
Constructors
| make | It-block construtor |
Fields
| autoCommit | autoCommit sets the autoCommit field on a connection just after it is opened by the pool. |
| linger | Time to linger an idle connection before closing it. |
| log | Logger |
| maxConns | Max number of simultaneous connections to allow before blocking threads |
| password | Connection password |
| timeout | Max time to block waiting for a connection before raising TimeoutErr |
| uri | Connection URI |
| username | Connection username |
Methods
| checkLinger | Close idle connections that have lingered past the linger timeout. |
| close | Close all connections and raise exception on any new executes |
| execute | Allocate a SQL connection inside the given callback. |
| isClosed | Return if close has been called. |
| onClose | onClose is invoked just before a connection is closed by the pool. |
| onOpen | onOpen is invoked just after a connection is opened by the pool. |
Slot Details
autoCommit
Bool autoCommit
autoCommit sets the autoCommit field on a connection just after it is opened by the pool.
If auto-commit is true then each statement is executed and committed as an individual transaction. Otherwise statements are grouped into transaction which must be closed via SqlConn.commit or SqlConn.rollback.
checkLinger
Void checkLinger()
Close idle connections that have lingered past the linger timeout.
close
Void close()
Close all connections and raise exception on any new executes
execute
Void execute(|SqlConn| f)
Allocate a SQL connection inside the given callback. If a connection cannot be acquired before timeout elapses then a TimeoutErr is raised. Do not close the connection inside the callback.
isClosed
Bool isClosed()
Return if close has been called.
linger
const Duration linger := 5min
Time to linger an idle connection before closing it. An external actor must call checkLinger periodically to close idle connetions.
log
const Log log := Log.get("sqlPool")
Logger
make
new make(|This|? f)
It-block construtor
maxConns
const Int maxConns := 10
Max number of simultaneous connections to allow before blocking threads
onClose
protected virtual Void onClose(SqlConn c)
onClose is invoked just before a connection is closed by the pool.
onOpen
protected virtual Void onOpen(SqlConn c)
onOpen is invoked just after a connection is opened by the pool.
password
const Str? password
Connection password
timeout
const Duration timeout := 30sec
Max time to block waiting for a connection before raising TimeoutErr
uri
const Str uri
Connection URI
username
const Str? username
Connection username
SqlErr
const class SqlErr : Err
SqlErr indicates an error from the SQL database driver.
Constructors
| make |
Slot Details
make
new make(Str? msg, Err? cause := null)
SqlMeta
class SqlMeta : Obj
SqlMeta provides access to database meta-data
Methods
| driverName | Name of connection driver to database |
| driverVersion | Version of of connection driver to database as "major.minor" |
| driverVersionStr | Version of of connection driver to database as free-form string |
| maxColName | Max number of chars in column name or null if no known limit |
| maxTableName | Max number of chars in table name or null if no known limit |
| productName | Name of database product |
| productVersion | Product version of database as "major.minor" |
| productVersionStr | Product version of database as free-form string |
| tableExists | Does the specified table exist in the database? |
| tableRow | Get a column meta-data for for the specified table as a prototype row instance. |
| tables | List the tables in the database. |
Slot Details
driverName
Str driverName()
Name of connection driver to database
driverVersion
Version driverVersion()
Version of of connection driver to database as "major.minor"
driverVersionStr
Str driverVersionStr()
Version of of connection driver to database as free-form string
maxColName
Int? maxColName()
Max number of chars in column name or null if no known limit
maxTableName
Int? maxTableName()
Max number of chars in table name or null if no known limit
productName
Str productName()
Name of database product
productVersion
Version productVersion()
Product version of database as "major.minor"
productVersionStr
Str productVersionStr()
Product version of database as free-form string
tableExists
Bool tableExists(Str tableName)
Does the specified table exist in the database?
tableRow
Row tableRow(Str tableName)
Get a column meta-data for for the specified table as a prototype row instance.
tables
Str[] tables()
List the tables in the database.
Statement
class Statement : Obj
Statement is an executable statement for a specific database. A statement may be executed immediately or prepared and executed later with parameters. See pod-doc.
Fields
| limit | Maximum number of rows returned when this statement is executed. |
| sql | The SQL text used to create this statement. |
Methods
| close | Close the statement. |
| execute | Execute a SQL statement and if applicable return a result |
| executeBatch | Execute a batch of commands on a prepared Statement. |
| prepare | Prepare this statement by compiling for efficient execution. |
| query | Execute the statement and return the resulting List of Rows. |
| queryEach | Execute the statement. |
| queryEachWhile | Execute the statement. |
Slot Details
close
Void close()
Close the statement.
execute
Obj execute([Str:Obj]? params := null)
Execute a SQL statement and if applicable return a result:
- If the statement is a query or procedure which produces a result set, then return
Row[] - If the statement is an insert and auto-generated keys are supported by the connector then return
Int[]orStr[]of keys generated - Return an
Intwith the update count
executeBatch
Int?[] executeBatch([Str:Obj]?[] paramsList)
Execute a batch of commands on a prepared Statement. If all commands execute successfully, returns an array of update counts.
For each element in the array, if the element is non-null, then it represents an update count giving the number of rows in the database that were affected by the command's execution.
If a given array element is null, it indicates that the command was processed successfully but that the number of rows affected is unknown.
If one of the commands in a batch update fails to execute properly, this method throws a SqlErr that wraps a java.sql.BatchUpdateException, and a JDBC driver may or may not continue to process the remaining commands in the batch, consistent with the underlying DBMS -- either always continuing to process commands or never continuing to process commands.
limit
Int? limit
Maximum number of rows returned when this statement is executed. If limit is exceeded rows are silently dropped. A value of null indicates no limit.
prepare
This prepare()
Prepare this statement by compiling for efficient execution. Return this.
query
Row[] query([Str:Obj]? params := null)
Execute the statement and return the resulting List of Rows. The Cols are available from List.of.fields or on type.fields of each row instance.
queryEach
Void queryEach([Str:Obj]? params, |Row| eachFunc)
Execute the statement. For each row in the result, invoke the specified function eachFunc.
queryEachWhile
Obj? queryEachWhile([Str:Obj]? params, |Row->Obj?| eachFunc)
Execute the statement. For each row in the result, invoke the specified function eachFunc. If the function returns non-null, then break the iteration and return the resulting object. Return null if the function returns null for every item.
sql
const Str sql
The SQL text used to create this statement.