Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of mappen te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen om mappen te wijzigen.
JDBC-stuurprogramma downloaden
Het Microsoft JDBC-stuurprogramma voor SQL Server bevat een alternatieve manier om metagegevens van parameters van de server op te vragen, useFmtOnly. Deze functie is voor het eerst geïntroduceerd in versie 7.4 van het stuurprogramma en is vereist als tijdelijke oplossing voor bekende problemen in sp_describe_undeclared_parameters.
Het stuurprogramma gebruikt voornamelijk de opgeslagen procedure sp_describe_undeclared_parameters om metagegevens van parameters op te vragen. Deze procedure is de aanbevolen methode voor het ophalen van parametermetagegevens in de meeste gevallen. Het uitvoeren van de opgeslagen procedure mislukt echter onder de volgende gebruiksscenario's:
- Op basis van Always Encrypted-kolommen
- Tegen tijdelijke tabellen en tabelvariabelen
- Tegen weergaven
De voorgestelde oplossing voor deze use cases is het parseren van de SQL-query van de gebruiker voor parameters en tabeldoelen en vervolgens een SELECT query uitvoeren waarvoor FMTONLY is ingeschakeld. Het volgende codefragment helpt bij het visualiseren van de functie.
--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;
De functie in-/uitschakelen
De functie useFmtOnly is standaard uitgeschakeld. Gebruikers kunnen deze functie inschakelen via de verbindingsreeks door op te useFmtOnly=truegeven. Voorbeeld: jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;useFmtOnly=true;.
De functie is ook beschikbaar via 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
}
De functie is ook beschikbaar op instructieniveau. Gebruikers kunnen de functie in-/uitschakelen via PreparedStatement.setUseFmtOnly(boolean).
Opmerking
Het stuurprogramma geeft prioriteit aan de eigenschap Instructieniveau boven de eigenschap Verbindingsniveau.
De functie gebruiken
Als dit is ingeschakeld, wordt de nieuwe functie intern gebruikt in plaats van bij het uitvoeren van sp_describe_undeclared_parameters query's op parametermetagegevens. Er is geen verdere actie nodig van de eindgebruiker.
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'
}
}
Opmerking
De functie ondersteunt SELECT/INSERT/UPDATE/DELETE alleen query's. Query's moeten beginnen met een van de vier ondersteunde sleutelwoorden of een Common Table Expression , gevolgd door een van de ondersteunde query's. Parameters binnen Common Table Expressions worden niet ondersteund.
Bekende problemen
Er zijn momenteel enkele problemen met de functie, die worden veroorzaakt door hiaten in de SQL-parseringslogica. Deze problemen kunnen worden opgelost in een toekomstige update van de functie en worden hieronder beschreven, samen met tijdelijke suggesties.
Eén. Een doorstuurde alias gebruiken
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. Niet-eenduidige kolomnaam wanneer tabellen gedeelde kolomnamen hebben
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 uit een subquery met parameters
CREATE TABLE Foo(c1 int)
SELECT * FROM (SELECT * FROM Foo WHERE c1 = ?) WHERE c1 = ?; --Incorrect syntax near '?'
--Workaround: N/A
D. Subquery's in een SET-component
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 = ?;