How can I persist Alter Session Set variables for a certain database only? - oracle

I want to change the NLS_SORT and NLS_COMP parameters for every query to this particular schema, but there are other users on this system as well that want to keep the original values, so using ALTER SYSTEM SET is a no-no in this case. I also don't want to change every query to this particular schema.
Is there a way to either put these values in an initialization file that is particular to only this schema or can I somehow add a trigger that sets these values on the session whenever a session is started to this schema?
I am running Oracle Express 11G R2 and the solution does not need to be backwards compatible.
My goal is to not have to run the ALTER SESSION SET rows before running the SELECT-LIKE-statement and having it produce two results rather than one. Here is the Java sample code that I've used to examine what values I actually want the NLS_COMP and NLS_SORT values to have:
public class OracleCaseTest {
public static void main(String[] args) throws SQLException {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:#localhost:1521:xe");
dataSource.setUsername("CASETEST");
dataSource.setPassword("casetest");
Connection conn = null;
PreparedStatement createStatement = null;
PreparedStatement populateStatement = null;
PreparedStatement comparisonAlterSessionStatement = null;
PreparedStatement sortAlterSessionStatement = null;
PreparedStatement queryStatement = null;
PreparedStatement deleteStatement = null;
ResultSet rs = null;
conn = dataSource.getConnection();
createStatement = conn
.prepareStatement("CREATE TABLE CollationTestTable ( Name varchar(255) )");
createStatement.execute();
try {
// comparisonAlterSessionStatement = conn
// .prepareStatement("ALTER SESSION SET NLS_COMP=LINGUISTIC");
// comparisonAlterSessionStatement.execute();
//
// sortAlterSessionStatement = conn.prepareStatement("ALTER SESSION SET NLS_SORT=BINARY_CI");
// sortAlterSessionStatement.execute();
String[] names = { "pepe", "pépé", "PEPE", "MEME", "mémé", "meme" };
for (String name : names) {
populateStatement = conn
.prepareStatement("INSERT INTO CollationTestTable VALUES (?)");
populateStatement.setString(1, name);
populateStatement.execute();
}
queryStatement = conn
.prepareStatement("SELECT Name FROM CollationTestTable WHERE NAME LIKE 'pe%'");
rs = queryStatement.executeQuery();
while (rs.next()) {
System.out.println(rs.getString(1));
}
} finally {
deleteStatement = conn.prepareStatement("DROP TABLE CollationTestTable");
deleteStatement.execute();
}
}
}
I'm aware of the problem with full table scans without linguistic indexes that this might create, but ignore that for this question.
UPDATE: This is the statement I used to create my trigger from the SQL command-line interface (after connecting and logging in with my user):
create or replace trigger nls_settings
after logon on schema
begin
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_COMP=LINGUISTIC';
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_SORT=BINARY_CI';
end nls_settings;
/
Also changed the original question to indicate that by "database", in the Oracle world I really meant "schema"/"user".

You can create trigger on
database startup or instance shutdown
user logon or logoff
http://download.oracle.com/docs/cd/B14117_01/server.101/b10743/triggers.htm#i6061

First off, can you clarify what you mean by "database"? In Oracle terminology, a database is the set of schemas that would be affected by an ALTER SYSTEM call. It is possible to have multiple databases on a single server but you can only have one XE database on a machine. If you are coming from a SQL Server background, what SQL Server calls a "database" is more similar to what Oracle calls a "schema".
Assuming that you really mean schema and not database, and assuming that the Oracle CASETEST user only interacts with tables in the one schema, I would second bpgergo's suggestion of a login trigger in the CASETEST schema.

Related

Ignore accents on search using linq to entities (EF) and Oracle 12.1

I need to perform a search on a table whith a string field that contains accents, many operators could be applied: start with, contains, equal, in the list ...
If I do a search for Müller I want retrieve also Mueller (ue is the translate of ü in German), the same for the other letters having accents, I know that it is possible to achieve this by modifiying the NLS_COMP and NLS_SORT
SQL> ALTER SESSION SET NLS_COMP=LINGUISTIC;
SQL> ALTER SESSION SET NLS_SORT=BINARY_AI;
I know also that it is possible to Collation at column level but this is availble only since 12.2 version,
Any idea please ?
Thank you for your help,
Bilel
I've used oracle NLS session parameters to resolve my issue.
if(condition == true)
AlterSortSession(context);
public void AlterSortSession(MyContext context)
{
var connection = (OracleConnection)context.Database.Connection;
connection.StateChange += AlterSortSession;
}
private static void AlterSortSession(object sender, StateChangeEventArgs e)
{
if (e.CurrentState != ConnectionState.Open)
return;
var connection = (OracleConnection)sender;
OracleGlobalization info = connection.GetSessionInfo();
info.Sort = "XGERMAN_DIN_AI";
info.Comparison = "LINGUISTIC";
connection.SetSessionInfo(info);
}
Documentation is available here for OracleGlobalization
This worked for me for EF4:
using (var context = new Entities()) {
// Set Case Insensitive, Accent Insensitive
var orcl = (OracleConnection)(((System.Data.EntityClient.EntityConnection)(context.Connection)).StoreConnection);
if (context.Connection.State != System.Data.ConnectionState.Open)
{
context.Connection.Open();
}
var sInfo = orcl.GetSessionInfo();
sInfo.Sort = "GENERIC_M_AI";
sInfo.Comparison = "LINGUISTIC";
orcl.SetSessionInfo(sInfo);
// Execute linq query
var row = context.table.Where(a => a.varcharField.Contains("match str")).FirstOrDefault();
}

Problem in updating a data column in spring

I have a Database table called ProgramData. their i have a data column called Id and executed. id set to be as auto increment.
Table structure is like this.
What i want is according to id executed column need to be updated. following is my code segment.
public void saveDtvProgDataExecuted()
{
ProgramData programeData = new ProgramData();
String SQL = "UPDATE program_data SET executed=1 WHERE programeData.id = ?";
this.jdbcTemplate.update(SQL);
}
If i run this code this gives me error like bad SQL grammar [UPDATE program_data SET executed=1 WHERE programeData.id = ?]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?' at line 1
Problem is you’re not passing the ID value to the jdbctemplate.
You should use
this.jdbctemplate.update(SQL, id);
Where id is the id of the record you’re updating.
Please refer to the documentation for more information:
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#jdbc-updates
TRY THIS statement while you are passing ? in your sql query it need to be set while execution.
String SQL = "UPDATE program_data SET executed=1 WHERE programeData.id = ?";
this.jdbcTemplate.update(SQL,new PreparedStatementCallback<Boolean>(){
#Override
public Boolean doInPreparedStatement(PreparedStatement ps)
throws SQLException, DataAccessException {
ps.setInt(1,"here you need to pass value of programeData.id);
return ps.execute();
}
});

BatchUpdateException:ORA-00942:TABLE OR VIEW DOES NOT EXIST

This issue occurred in jdbc batch insert. I queried from an Oracle datasource, parsed the resultset and then inserted into another Oracle datasource. I have got the connect metadata and printed the current username along with url, both are invalid.
But when it went to batch update, I got the ora-00942 exception. I'm pretty sure all above works fine in database. Has anyone encountered this exception and can you give me some advice?
EDIT:
Ok, I got a table named photos for example in REMOTE_USER and I queried from it. It gave me a resultset, then I parse it after that INSERT it to LOCAL_USER.photos. I did query the LOCAL_USER.photos where I logon in from PL/SQL Developer. The interesting thing was I could do the select command but not the insert. Below is some part of code.
conn = datasource.getConnection(); // notice that it was target datasource
DatabaseMetaData connMetaData = conn.getMetaData();
String userName = connMetaData.getUserName();
resultSet = ds.getResultSet();
ResultSetMetaData metaData = resultSet.getMetaData();
int count = metaData.getColumnCount();
String insertSql = generateInsertSql(count, metaData, userName);
// this was generated through metaData , the output should be
// "insert into LOCAL_USER.photos(col1,col2) values(?,...)"
logger.error("insert clause is {}", insertSql);
ps = conn.prepareStatement(insertSql);
conn.setAutoCommit(false);
while (resultSet.next()) { // this was the original datasource
stageTotalNum++;
for (int i = 1; i <= count; i++) {
Object object = resultSet.getObject(i);
dealClobColumn(ps, i, object);
}
ps.addBatch();
if (stageTotalNum % 500L == 0L) {
ps.executeBatch(); // throws batchupdateexception.
ps.clearBatch();
conn.commit();
}
}
ps.executeBatch();
conn.commit();
It should be the blob type column which I didn't handle it the right way.
First I queried from original datasource then got the blob column of the resultset by
conn.getObject(index) . Next I insert the blob column into target datasource by conn.setObject. Of course that way wasn't working at all, so I changed to the following:
conn.setBlob(rs.getBlob(index)).
Although it worked fine in my own environemnt, but when the application ran in remote server, it kept annoying about the 'table or view does not exists'.The third version is:
conn.setBinaryStream(rs.getBlob(index).getBinaryStream());
Ok, this time it worked both my pc and remote server. Thanks to #codeLover's advice and link, it really hepled me and saved my time. Appreciated it!

How to retrive a generated primary key when a new row is inserted in the oracle database using Spring JDBC?

Below is the code I am using to save a record in the database and then get the generated primary key.
public void save(User user) {
// TODO Auto-generated method stub
Object[] args = { user.getFirstname(), user.getLastname(),
user.getEmail() };
int[] types = { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR };
SqlUpdate su = new SqlUpdate();
su.setJdbcTemplate(getJdbcTemplate());
su.setSql(QUERY_SAVE);
setSqlTypes(su, types);
su.setReturnGeneratedKeys(true);
su.compile();
KeyHolder keyHolder = new GeneratedKeyHolder();
su.update(args, keyHolder);
int id = keyHolder.getKey().intValue();
if (su.isReturnGeneratedKeys()) {
user.setId(id);
} else {
throw new RuntimeException("No key generated for insert statement");
}
}
But its not working, It gives me following error.
The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
The row is being inserted in the database properly. As well I could get the generataed primary key when using MS SQL database but the same code is not working with the ORACLE 11G.
Please help.
As in the comment, oracle rowid's are alpha numerical so can't be cast to an int.
Besides that, you should not use the generated rowid anywhere in your code. This is not the primary key that you defined on the table.
MS SQL has the option to declare a column as a primary key which auto-increments. This is a functionality that does not work in oracle.
What I always do (regardless if the db supports auto-increment) is the following:
select sequenceName.nextval from dual
The value returned by the previous statement is used as the primary key for the insert statement.
insert into something (pk, ...) values (:pk,:.....)
That way we always have the pk after the insert.

Resultset Metadata from Spring JDBCTemplate Query methods

Is there any way I can get resultset object from one of jdbctemplate query methods?
I have a code like
List<ResultSet> rsList = template.query(finalQuery, new RowMapper<ResultSet>() {
public ResultSet mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs;
}
}
);
I wanted to execute my sql statement stored in finalQuery String and get the resultset. The query is a complex join on 6 to 7 tables and I am select 4-5 columns from each table and wanted to get the metadata of those columns to transform data types and data to downstream systems.
If it is a simple query and I am fetching form only one table I can use RowMapper#mapRow and inside that maprow method i can call ResultsetExtractor.extractData to get list of results; but in this case I have complex joins in my query and I am trying to get resultset Object and from that resultset metadata...
The above code is not good because for each result it will return same resultset object and I dont want to store them in list ...
Once more thing is if maprow is called for each result from my query will JDBCTemplate close the rs and connection even though my list has reference to RS object?
Is there any simple method like jdbcTemplate.queryForResultSet(sql) ?
Now I have implemented my own ResultSet Extractor to process and insert data into downstream systems
sourceJdbcTemplate.query(finalQuery, new CustomResultSetProcessor(targetTable, targetJdbcTemplate));
This CustomResultSetProcessor implements ResultSetExtractor and in extractData method I am calling 3 different methods 1 is get ColumnTypes form rs.getMetaData() and second is getColumnTypes of target metadata by running
SELECT NAME, COLTYPE, TBNAME FROM SYSIBM.SYSCOLUMNS WHERE TBNAME ='TABLENAME' AND TABCREATOR='TABLE CREATOR'
and in 3rd method I am building the insert statement (prepared) form target columntypes and finally calling that using
new BatchPreparedStatementSetter()
{
#Override
public void setValues(PreparedStatement insertStmt, int i) throws SQLException{} }
Hope this helps to others...
Note that the whole point of Spring JDBC Template is that it automatically closes all resources, including ResultSet, after execution of callback method. Therefore it would be better to extract necessary data inside a callback method and allow Spring to close the ResultSet after it.
If result of data extraction is not a List, you can use ResultSetExtractor instead of RowMapper:
SomeComplexResult r = template.query(finalQuery,
new ResultSetExtractor<SomeComplexResult>() {
public SomeResult extractData(ResultSet) {
// do complex processing of ResultSet and return its result as SomeComplexResult
}
});
Something like this would also work:
Connection con = DataSourceUtils.getConnection(dataSource); // your datasource
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(query); // your query
ResultSetMetaData rsmd = rs.getMetaData();
Although I agree with #axtavt that ResultSetExtractor is preferred in Spring environment, it does force you to execute the query.
The code below does not require you to do so, so that the client code is not required to provide the actual arguments for the query parameters:
public SomeResult getMetadata(String querySql) throws SQLException {
Assert.hasText(querySql);
DataSource ds = jdbcTemplate.getDataSource();
Connection con = null;
PreparedStatement ps = null;
try {
con = DataSourceUtils.getConnection(ds);
ps = con.prepareStatement(querySql);
ResultSetMetaData md = ps.getMetaData(); //<-- the query is compiled, but not executed
return processMetadata(md);
} finally {
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, ds);
}
}

Resources