Sdílet prostřednictvím


Načítání metadat parametrů pomocí useFmtOnly

Stáhnout ovladač JDBC

Ovladač Microsoft JDBC pro SQL Server obsahuje alternativní způsob dotazování metadat parametrů ze serveru , useFmtOnly. Tato funkce byla poprvé představena ve verzi 7.4 ovladače a je vyžadována jako alternativní řešení známých problémů v sp_describe_undeclared_parameters.

Ovladač primárně používá uloženou proceduru sp_describe_undeclared_parameters k dotazování metadat parametrů. Tento postup je doporučeným přístupem k načítání metadat parametrů za většiny okolností. Provádění uložené procedury však v současné době selže v následujících případech použití:

  • Proti sloupcům Always Encrypted
  • Proti dočasným tabulkám a proměnným tabulek
  • Proti názorům

Pro tyto případy použití je navrhované řešení zpracovat uživatelův dotaz SQL, identifikovat parametry a cíle tabulky, a poté spustit dotaz s povolenou funkcí SELECTFMTONLY. Následující fragment kódu vám pomůže vizualizovat funkci.

--create a normal table 'Foo' and a temporary table 'Bar'
CREATE TABLE Foo(c1 int);
CREATE TABLE #Bar(c1 int);

EXEC sp_describe_undeclared_parameters N'SELECT * FROM Foo WHERE c1 = @p0' --works fine
EXEC sp_describe_undeclared_parameters N'SELECT * FROM #Bar WHERE c1 = @p0' --fails with "Invalid object name '#Bar'"

SET FMTONLY ON;
SELECT c1 FROM Foo; --works
SET FMTONLY OFF;
SET FMTONLY ON;
SELECT c1 FROM #Bar; --works
SET FMTONLY OFF;

Zapnutí nebo vypnutí funkce

Funkce useFmtOnly je ve výchozím nastavení vypnutá. Uživatelé mohou tuto funkci povolit prostřednictvím připojovacího řetězce zadáním useFmtOnly=true. Například: jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;useFmtOnly=true;.

Tato funkce je také k dispozici prostřednictvím SQLServerDataSource.

SQLServerDataSource ds = new SQLServerDataSource();
ds.setServerName(<server>);
ds.setPortNumber(<port>);
ds.setDatabaseName("<databaseName>");
ds.setUser("<user>");
ds.setPassword("<password>");
ds.setUseFmtOnly(true);
try (Connection c = ds.getConnection()) {
    // do work with connection
}

Tato funkce je dostupná také na úrovni výkazu. Uživatelé můžou tuto funkci zapnout nebo vypnout prostřednictvím PreparedStatement.setUseFmtOnly(boolean)funkce .

Poznámka:

Ovladač upřednostní vlastnost Úroveň výpisu nad vlastností Úroveň připojení.

Použití funkce

Po povolení začne ovladač interně používat novou funkci místo sp_describe_undeclared_parameters při dotazování metadat parametrů. Koncový uživatel nevyžaduje žádnou další akci.

final String sql = "INSERT INTO #Bar VALUES (?)";
try (Connection c = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
    try (Statement s = c.createStatement()) {
        s.execute("CREATE TABLE #Bar(c1 int)");
    }
    try (PreparedStatement p1 = c.prepareStatement(sql); PreparedStatement p2 = c.prepareStatement(sql)) {
        ((SQLServerPreparedStatement) p1).setUseFmtOnly(true);
        ParameterMetaData pmd1 = p1.getParameterMetaData();
        System.out.println(pmd1.getParameterTypeName(1)); // prints int
        ParameterMetaData pmd2 = p2.getParameterMetaData(); // throws exception, Invalid object name '#Bar'
    }
}

Poznámka:

Tato funkce podporuje pouze SELECT/INSERT/UPDATE/DELETE dotazy. Dotazy by měly začínat jedním ze 4 podporovaných klíčových slov nebo výrazem společné tabulky následovanými jedním z podporovaných dotazů. Parametry v rámci Common Table Expressions nejsou podporovány.

Známé problémy

V současné době dochází k problémům s funkcí, které jsou způsobené mezerami v logice analýzy SQL. Tyto problémy mohou být vyřešeny v budoucí aktualizaci funkce a jsou popsané níže spolu s návrhy alternativního řešení.

A. Použití dopředu deklarovaného aliasu

CREATE TABLE Foo(c1 int)

DELETE fooAlias FROM Foo fooAlias WHERE c1 > ?; --Invalid object name 'fooAlias'

--Workaround #1: Specify AS keyword
DELETE fooAlias FROM Foo AS fooAlias WHERE c1 > ?;
--Workaround #2: Use the table name
DELETE Foo FROM Foo fooAlias WHERE c1 > ?;

B. Nejednoznačný název sloupce, pokud mají tabulky názvy sdílených sloupců

CREATE TABLE Foo(c1 int, c2 int, c3 int)
CREATE TABLE Bar(c1 int, c2 int, c3 int)

SELECT c1,c2 FROM Foo WHERE c3 IN (SELECT c3 FROM Bar WHERE c1 > ? and c2 < ? and c3 = ?); --Ambiguous Column Name

--Workaround: Use aliases
SELECT c1,c2 FROM Foo WHERE c3 IN (SELECT c3 FROM Bar b WHERE b.c1 = ? and b.c2 = ? and b.c3 = ?);

C. SELECT z poddotazů s parametry


CREATE TABLE Foo(c1 int)

SELECT * FROM (SELECT * FROM Foo WHERE c1 = ?) WHERE c1 = ?; --Incorrect syntax near '?'

--Workaround: N/A

D. Poddotazy v klauzuli SET

CREATE TABLE Foo(c1 int)

UPDATE Foo SET c1 = (SELECT c1 FROM Foo) WHERE c1 = ?; --Incorrect syntax near ')'

--Workaround: Add a 'delimiting' condition
UPDATE Foo SET c1 = (SELECT c1 FROM Foo HAVING (HASH JOIN)) WHERE c1 = ?;

Viz také

Nastavení vlastností připojení