How to get Column Comments in JDBC - java

I want to fetch Column comments using JDBC Metadata , But everytime it returns null , I tested with Oracle and SqlServer both cases it returning Null.
DatabaseMetaData dmt = con.getMetaData();
colRs = dmt.getColumns(null, "dbo", 'Student', null);
while (colRs.next()) {
System.out.println(colRs.getString("REMARKS");
}
While i am getting all other data like column name , length etc absolutely ok ...

For Oracle you need to provide a connection property remarksReporting and set that to true or call the method setRemarksReporting() to enable that.
OracleConnection oraCon = (OracleConnection)con;
oraCon.setRemarksReporting(true);
After that, getColumns() will return the column (or table) comments in the REMARKS column of the ResultSet.
See Oracle's JDBC Reference for more details
For SQL Server this is not possible at all.
Neither the Microsoft nor the jTDS driver expose table or column comments. Probably because there is no SQL support for that in SQL Server. The usual approach of using "extended properties" and the property name MS_DESCRIPTION is not reliable. Mainly because there is no requirement to us MS_DESCRIPTION as the property name. Not even sp_help returns those remarks. And at least the jTDS driver simply calls sp_help go the the table columns. I don't know what the Microsoft driver does.
The only option you have there, is to use fn_listextendedproperty() to retrieve the comments:
e.g.:
SELECT objname, cast(value as varchar(8000)) as value
FROM fn_listextendedproperty ('MS_DESCRIPTION','schema', 'dbo', 'table', 'Student', 'column', null)
You need to replace MS_DESCRIPTION with whatever property name you use to store your comments.

Related

utf8_unicode_ci String is inserted incorrectly?

I have java application through which I do different operations on MySQL DB. The probleam is that when inserting utf8 String it is not inserted correctly. The charset of DB is utf8 and I have set collation to utf8_unicode_ci. Server connection collation is also utf8_unicode_ci. Furthermore when I insert data from phpMyAdmin it is inserted correctly, but when I do it from Java application using JOOQ - it is not. Example:
Result<ExecutorsRecord> executorsRecord =
context.insertInto(EXECUTORS, EXECUTORS.ID, EXECUTORS.NAME, EXECUTORS.SURNAME, EXECUTORS.REGION, EXECUTORS.PHONE, EXECUTORS.POINTS, EXECUTORS.E_TYPE)
.values(id, name, surname, region, phone, 0, type)
.returning(EXECUTORS.ID)
.fetch();
where name = "Бобр" and surname = "Добр", produces tuple with ???? as a name and ???? as surname. I have checked both strings, they are passed correctly to the method correctly.
As #spencer7593 suggested the problem could be in JDBC connector. So I added into url of connection following: ?characterEncoding=utf8 so that final url was "jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8", where mydb is a name of database. This has sorted out my problem. Also I would like to add the following statement (again by #spencer7593):
When we've got things configured correctly, and things aren't working, our goto suspect is the JDBC driver. To get timezone differences between the JVM and the MySQL database sorted out, to prevent the JDBC driver from "helping" by doing an illogical combination of various operations, we had to add two extra obscurely documented settings to the connection string.
Further reading

Sqlserver: what are the differences between execute sql with jdbc driver and execute with sql client

I have a table named "T_ROLE", it has just one column named "NAME" which type is nvarchar(255), the sqlserver Collation is "SQL_Latin1_General_CP1_CI_AS"(en_US), now i want to insert japanese character, so i know that i need do sql like this:
INSERT INTO T_ROLE(NAME) VALUES(N'japaneseString')
this can be successful.
if i do sql:
INSERT INTO T_ROLE(NAME) VALUES('japaneseString')
which without N prefix, it will saved as '?', i can under these behavior.
But when i use sqlserver jdbc driver to do insert operation like this:
String sql = "INSERT INTO T_ROLE (NAME) VALUES(?)";
stmt.setString(1, "");
stmt.execute(sql);
notice: i don't use stmt.setNString() method, but it can be saved successful, why?
See this blog: https://blogs.msdn.microsoft.com/sqlcat/2010/04/05/character-data-type-conversion-when-using-sql-server-jdbc-drivers/
It turns out that the JDBC driver sends character data including varchar() as nvarchar() by default. The reason is to minimize client side conversion from Java’s native string type, which is Unicode.
So how do you force the JDBC driver not to behave this way? There is a connection string property, named sendStringParametersAsUnicode. By default it’s true.
One would ask what if I want to pass both varchar and nvarchar parameters at the same time? Well, even with the connection property set false, you can explicitly specify nvarchar type like this:
pStmt.setObject(2,Id,Types.NVARCHAR); //Java app code
Simple Google search for sql server jdbc nvarchar found this answer.

GetColumName(i) returns "" when using UNION (Jaybird)

I'm a little bit confused with my new SQL query using a union.
I request my firebird db in a java app using jaybird 2.2.8. Usually I parse my ResultSet using the MetaData
ResultSetMetaData metaData = resultSet.getMetaData();
and looping through the columns
for (int i = 1; i <= metaData.getColumnCount(); i++) {
columnName = metaData.getColumnName(i);
int columnType = metaData.getColumnType(i);
switch (columnType) { ...
This was working quite well until I started using a Union in my SQL query.
Now the method
metaData.getColumnName(i)
returns an empty string instead of the column name - the column type is valid.
When I use a SQL query without Union everything works as expected and when I test my query in IBExpert all columns have a a valid name.
Any idea whats wrong? Does anybody has a workaround?
Btw. the ResultSet looks quite well in the eclipse debugger
Instead of the getColumnName you need to use getColumnLabel. The column name only has a value if the value is guaranteed to come from a single column in a single table, which is not the case in a UNION. In the case of a UNION, Firebird handles the name of columns (or the alias if specified) in the first select as aliases (labels) only; it doesn't preserve the column name as a column name that can be returned by getColumnName.
If you really need to use getColumnName (eg for compatibility reasons), then you can use the Jaybird connection property columnLabelForName=true, but this is discouraged because it is not compliant with JDBC. In almost all cases you should use getColumnLabel instead.
The distinction between column name and column label is subtle, and wasn't really clear in older JDBC specification. Basically column name is the name of the column in the original table, while the column label is the 'name' (or more correctly: label) of the column in the result set. The label is either the alias specified with AS, or - if no alias is specified - the original column name as returned by getColumnName.
See also:
Changes and fixes in Jaybird 2.2.1
Compatibility with com.sun.rowset.*
Disclosure: I am one of the Jaybird developers

Why do I see NotUpdatable when I invoke ResultSet.refreshRow()?

When I invoke following rows:
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet resultSet = statement.executeQuery("select * from user");
resultSet.next();
resultSet.refreshRow();//exception throws here
I see following exception:
com.mysql.jdbc.NotUpdatable: Result Set not updatable.This result set
must come from a statement that was created with a result set type of
ResultSet.CONCUR_UPDATABLE, the query must select only one table, can
not use functions and must select all primary keys from that table.
See the JDBC 2.1 API Specification, section 5.6 for more details.This
result set must come from a statement that was created with a result
set type of ResultSet.CONCUR_UPDATABLE, the query must select only one
table, can not use functions and must select all primary keys from
that table. See the JDBC 2.1 API Specification, section 5.6 for more
details.
I wondered this exception because if read refreshRow method javadoc we can find following:
The refreshRow method provides a way for an application to explicitly
tell the JDBC driver to refetch a row(s) from the database
Thus following direction: database --> ResultSet
I have following understanding:
updatable is possibility to use following direction:
ResultSet --> database
Thus I don't understand cause of problem.
please clarify.
When you are using, ResultSet.CONCUR_READ_ONLY, you are getting an exception, that
com.mysql.jdbc.NotUpdatable: Result Set not updatable.This result set
must come from a statement that was created with a result set type of
ResultSet.CONCUR_UPDATABLE, the query must select only one table, can
not use functions and must select all primary keys from that table.
See the JDBC 2.1 API Specification, section 5.6 for more details.\
So, changing ResultSet.CONCUR_READ_ONLY to ResultSet.CONCUR_UPDATABLE, solved your problem. Correct?
Now, as I understand, you don't want to do that. Am I right? If yes, then I think you are confusing ResultSet with Database,
ResultSet.CONCUR_READ_ONLY, means read only ResultSet, not Database, similarly
ResultSet.CONCUR_UPDATABLE, means updatable ResultSet, not Database.
The method, resultSet.refreshRow(), suppose to update the resultSet, hence it rightly requires updatable ResultSet. I hope it's clear now.

JDBC DatabaseMetaData.getColumns() returns duplicate columns

I'm busy on a piece of code to get alle the column names of a table from an Oracle database. The code I came up with looks like this:
DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:#<server>:1521:<sid>", <username>, <password>);
DatabaseMetaData meta = conn.getMetaData();
ResultSet columns = meta.getColumns(null, null, "EMPLOYEES", null);
int i = 1;
while (columns.next())
{
System.out.printf("%d: %s (%d)\n", i++, columns.getString("COLUMN_NAME"),
columns.getInt("ORDINAL_POSITION"));
}
When I ran this code to my surprise too many columns were returned. A closer look revealed that the ResultSet contained a duplicate set of all the columns, i.e. every column was returned twice. Here's the output I got:
1: ID (1)
2: NAME (2)
3: CITY (3)
4: ID (1)
5: NAME (2)
6: CITY (3)
When I look at the table using Oracle SQL Developer it shows that the table only has three columns (ID, NAME, CITY). I've tried this code against several different tables in my database and some work just fine, while others exhibit this weird behaviour.
Could there be a bug in the Oracle JDBC driver? Or am I doing something wrong here?
Update: Thanks to Kenster I now have an alternative way to retrieve the column names. You can get them from a ResultSet, like this:
DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:#<server>:1521:<sid>", <username>, <password>);
Statement st = conn.createStatement();
ResultSet rset = st.executeQuery("SELECT * FROM \"EMPLOYEES\"");
ResultSetMetaData md = rset.getMetaData();
for (int i=1; i<=md.getColumnCount(); i++)
{
System.out.println(md.getColumnLabel(i));
}
This seems to work just fine and no duplicates are returned! And for those who wonder: according to this blog you should use getColumnLabel() instead of getColumnName().
In oracle, Connection.getMetaData() returns meta-data for the entire database, not just the schema you happen to be connected to. So when you supply null as the first two arguments to meta.getColumns(), you're not filtering the results for just your schema.
You need to supply the name of the Oracle schema to one of the first two parameters of meta.getColumns(), probably the second one, e.g.
meta.getColumns(null, "myuser", "EMPLOYEES", null);
It's a bit irritating having to do this, but that's the way the Oracle folks chose to implement their JDBC driver.
This doesn't directly answer your question, but another approach is to execute the query:
select * from tablename where 1 = 0
This will return a ResultSet, even though it doesn't select any rows. The result set metadata will match the table that you selected from. Depending on what you're doing, this can be more convenient. tablename can be anything that you can select on--you don't have to get the case correct or worry about what schema it's in.
In the update to your question I noticed that you missed one key part of Kenster's answer. He specified a 'where' clause of 'where 1 = 0', which you don't have. This is important because if you leave it off, then oracle will try and return the ENTIRE table. And if you don't pull all of the records over, oracle will hold unto them, waiting for you to page through them. Adding that where clause still gives you the metadata, but without any of the overhead.
Also, I personally use 'where rownum < 1', since oracle knows immediately that all rownums are past that, and I'm not sure if it's smart enough to not try and test each record for '1 = 0'.
In addition to skaffman's answer -
use the following query in Oracle:
select sys_context( 'userenv', 'current_schema' ) from dual;
to access your current schema name if you are restricted to do so in Java.
This is the behavior mandated by the JDBC API - passing nulls as first and second parameter to getColumns means that neither catalog name nor schema name are used to narrow the search.
Link to the documentation . It is true that some other JDBC drivers have different behavior by default (e.g MySQL's ConnectorJ by default restricts to the current catalog), but this is not standard, and documented as such

Categories