Spring - How to configure OracleDataSource from spring.datasource in application.properties - java

In my application.properties i have some spring.datasource fields
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.datasource.url=jdbc:oracle:thin:#localhost:1521:XE
spring.datasource.username=talon
spring.datasource.password=talon
These should be retrieved from a #Configuration annotated class
#Configuration
public class Db {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setUrl(String url) {
this.url = url;
}
public Db() {
OracleDataSource dataSource = null;
try {
dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select * from BOOK");
rs.next();
System.out.println(rs.getString(2));
} catch (SQLException e) {
e.printStackTrace();
}
}
}
But I'm unable to retrieve the username password and url from there, should I add another annotation somewhere or what am I doing
Like this I have the error:
java.sql.SQLException: Invalid Oracle URL specified: OracleDataSource.makeURL
If I set the proper url with dataSource.setURL("jdbc:oracle:thin:#localhost:1521:XE"); it can't read the username and password since they are null and I have java.sql.SQLException: ORA-01017: invalid username/password; logon denied

You have two issues:
You need to use another annotation in order for your fields to be populated. So annotate the class with #ConfigurationProperties("spring.datasource")
You cannot initialize your OracleDataSource directly in your constructor (i.e. Db()) because the properties (your username/password/url fields) are not populated during the call of the constructor, so one thing that you can do is create another method and annotate that with #PostContruct in order for your dataSource to be correctly created.
An example:
#PostConstruct
private void init() {
// your data source initialization
}
One advice would be to change the way to initialize your dataSource and instead of trying to create it inside your constructor, you can rather create a new method which you can annotate with #Bean and make it return your dataSource and you can use it later using #Autowired.

Related

how to get values to spring.boot.admin.client.username/ password from database?

I have spring boot admin project and now I hardcoded the username and passwords in application.properties file like this.
spring.boot.admin.client.username=user
spring.boot.admin.client.password=pass
spring.boot.admin.client.instance.metadata.user.name=user
spring.boot.admin.client.instance.metadata.user.password=pass
But want to get that values from database not hardcoded like this.I want to configs to connect to self register the admin server as a client.I am beginner to SpringBoot. How can I do it? Thanks.
So every configuration in an application.properties file can be configured via Javacode. First you have to create a Datasource for your project. Add the spring-data-jpa dependency to your project and config the datasource.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
more you can find here: A Guide to JPA with Spring
To configure for example the two properties spring.boot.admin.client.username=user and spring.boot.admin.client.password=pass you need to create a #Configuration class which creates a ClientProperties Bean.
#Configuration
public class AdminClientConfig {
private final JdbcTemplate jdbcTemplate;
private final Environment environment;
public AdminClientConfig(JdbcTemplate jdbcTemplate,
Environment environment) {
super();
this.jdbcTemplate = jdbcTemplate;
this.environment = environment;
}
#Bean
public ClientProperties clientProperties() {
ClientProperties cp = new ClientProperties(environment);
cp.setUsername(getUsername());
cp.setPassword(getPassword());
return cp;
}
private String getUsername() {
String username = jdbcTemplate.queryForObject(
"select username from AnyTable where id = ?",
new Object[] { "123" }, String.class);
return username;
}
private String getPassword() {
String password = jdbcTemplate.queryForObject(
"select password from AnyTable where id = ?",
new Object[] { "123" }, String.class);
return password;
}
}
So the JdbcTemplate has already a Database connection and creates the query to get the Username and Password from the Database. The ClientProperties Bean can then be set.
P.S.: This code is not tested but gives you a some hints to get the job done.

Using application.properties in #Configuration class, #Bean method now working

I have the below configuration class definition in java springboot. However, it fails for reference to property values.
#org.springframework.context.annotation.Configuration
public class HbaseConfig {
#Value("${keytab.user.name}")
private String username;
#Value("${keytab.path}")
private String keytabpath;
#Bean
public Connection getHbaseConnect() throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
UserGroupInformation.setConfiguration(conf);
System.out.println("hbase connect..is connection closed..." + connection.isClosed());
UserGroupInformation.loginUserFromKeytabAndReturnUGI(username, keytabpath);
return connection;
}
#Bean
public Admin getHbaseAdmin(Connection connection) throws IOException{
Admin admin = connection.getAdmin();
return admin;
}
}
application.properties
keytab.user.name="username"
keytab.path="pathtokeytab"
To put it simple, I need the above keytab username and path read from a property file in my HbaseConfig class.
Can you please try out the below method,
#ConfigurationProperties(prefix = "keytab")
public class KeyTabConfig {
private String username;
private String path;
public String getUsername(){ return this.username; }
public String getPath(){ return this.path;}
}
Habseconfig class as follows
#Configuration
#EnableConfigurationProperties({ KeyTabConfig.class})
public class HbaseConfig {
#Bean
public Connection getHbaseConnect(KeyTabConfig keyTabConfig) throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection connection =
ConnectionFactory.createConnection(conf);
UserGroupInformation.setConfiguration(conf);
System.out.println("hbase connect..is connection closed..." +
connection.isClosed());
UserGroupInformation.loginUserFromKeytabAndReturnUGI(keyTabConfig.getUsername(), keyTabConfig.getPath());
return connection;
}
#Bean
public Admin getHbaseAdmin(Connection connection) throws IOException{
Admin admin = connection.getAdmin();
return admin;
}
}
application.properties file as
keytab.username=uname
keytab.path=path
Make sure your property file name should application.properties and location should in
src/main/resources/application.properties
used only #Configuration at the class level and also use #PropertySource to define the location of our properties file.#PropertySource("classpath:configprops.properties") if your property file name is different.
Otherwise, Spring uses the default location (classpath:application.properties).
The actual processing of #Value annotation is performed by BeanPostProcessor and so we cannot use #Value within BeanPostProcessor types.
Examples: https://www.concretepage.com/spring-5/spring-value#placeholder
Link: How exactly does the Spring BeanPostProcessor work?

SQL SERVER 2008- Unable to get the object definition for few objects in ddl schema?

I am using MS SQL SERVER 2008 database in which i create all objects like procedure,functions,views etc. Now create a sample program get all objects definition(ddl), Here i found that some of objects definition is null i used following sql query to get definition of all procedures:
Select object_definition(object_id) from sys.objects where type = 'P'
Here are my sample code:
public class sam {
String userName;
String dbURL;
String password;
public sam(String dbURL, String userName, String password) {
this.dbURL = dbURL;
this.userName = userName;
this.password = password;
}
public void createSQLFile() throws IOException, SQLException, ClassNotFoundException {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection(dbURL, userName, password);
Statement statement = con.createStatement();
String queryString = "Select object_definition(object_id) from sys.objects where type = 'P'";
ResultSet rs = statement.executeQuery(queryString);
while (rs.next()) {
System.out.println(rs.getString(1));
}
}
public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {
String dbURl = "jdbc:sqlserver://localhost:1433;DatabaseName=db";
String userName = "u";
String password = "P";
new sam(dbURl, userName, password).createSQLFile();
}
} Also i have attach a screen shots of my database where procedure listed (Note: The highlighted procedure deification are null).
Can anyone suggest- How to get these object defination as they are encrypted?
MSDN states
The definition of user objects is visible to the object owner or grantees that have any one of the following permissions:
ALTER,
CONTROL,
TAKE OWNERSHIP,
or VIEW DEFINITION.

Use custom creds while using DropWizard JDBI

I'm developing a webservice using Dropwizard JDBI framework.
Now, instead of having a db configurations in yaml file, I want to use 'user specified params' what i mean to say is, the db configs will be provided through the endpoint url.
Is having custom creds possible through dropwizard jdbi?
if yes, what changes should i be thinking to do in the code while referring this ? ->
http://dropwizard.readthedocs.org/en/latest/manual/jdbi.html
I understand, in normal flow, the service method gets the config details in the run method -
-- Config Class
public class ExampleConfiguration extends Configuration {
#Valid
#NotNull
#JsonProperty
private DatabaseConfiguration database = new DatabaseConfiguration();
public DatabaseConfiguration getDatabaseConfiguration() {
return database;
}
}
-- Service Class
#Override
public void run(ExampleConfiguration config,
Environment environment) throws ClassNotFoundException {
final DBIFactory factory = new DBIFactory();
final DBI jdbi = factory.build(environment, config.getDatabaseConfiguration(), "postgresql");
final UserDAO dao = jdbi.onDemand(UserDAO.class);
environment.addResource(new UserResource(dao));
}
-- and yaml
database:
# the name of your JDBC driver
driverClass: org.postgresql.Driver
# the username
user: pg-user
# the password
password: iAMs00perSecrEET
# the JDBC URL
url: jdbc:postgresql://db.example.com/db-prod
But in this case, I might get the config details in the Resource level...
smthing like -
#GET
#Path(value = "/getProduct/{Id}/{dbUrl}/{dbUname}/{dbPass}")
#Produces(MediaType.APPLICATION_JSON)
public Product getProductById(#PathParam(value = "Id") int Id,
#PathParam(value = "dbUrl") String dbUrl,
#PathParam(value = "dbUname") String dbUname,
#PathParam(value = "dbPath") String dbPass) {
//I have to connect to the DB here! using the params i have.
return new Product(); //should return the Product
}
I'd appreciate if someone can point me a direction.
Why not just use JDBI directly?
#GET
#Path(value = "/getProduct/{Id}/{dbUrl}/{dbUname}/{dbPass}")
#Produces(MediaType.APPLICATION_JSON)
public Product getProductById(#PathParam(value = "Id") int id,
#PathParam(value = "dbUrl") String dbUrl,
#PathParam(value = "dbUname") String dbUname,
#PathParam(value = "dbPass") String dbPass) {
DataSource ds = JdbcConnectionPool.create(dbUrl, dbUname, dbPass);
DBI dbi = new DBI(ds);
ProductDAO dao = dbi.open(ProductDao.class);
Product product = dao.findById(id);
dao.close();
ds.dispose();
return product;
}
#RegisterMapper(ProductMapper.class)
static interface ProductDao {
#SqlQuery("select id from product_table where id = :id") // Whatever SQL query you need to product the product
Product findById(#Bind("id") int id);
#SqlQuery("select * from product_table")
Iterator<Product> findAllProducts();
}
static class ProductMapper implements ResultSetMapper<Product> {
public Product map(int index, ResultSet r, StatementContext ctx) throws SQLException {
return new Product(r.getInt("id")); // Whatever product constructor you need
}
}
There's a notion in the spring world of using a database router (reference: https://spring.io/blog/2007/01/23/dynamic-datasource-routing/).
You likely could setup a proxy for the database connection factory passed to DBI. That proxy would then get the credentials from a thread local (perhaps) and return the real connection giving you what you're after and still let you use the run type proxies.

java.sql.SQLException: This function is not supported using HSQL and Spring

Can someone please tell me why am I geting java.sql.SQLException: This function is not supported using HSQL and Spring? I am trying to insert a new row into my database..
Below is my DAO and I get the error on the mySession.save(message) line:
#Transactional
#Repository
public class MessageDaoImpl implements MessageDao
{
private Log log = null;
#Autowired
private SessionFactory sessionFactory;
public MessageDaoImpl()
{
super();
log = LogFactory.getLog(MessageDaoImpl.class);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public List<Message> listMessages()
{
try
{
return (List<Message>) sessionFactory.getCurrentSession()
.createCriteria(Message.class).list();
} catch (Exception e)
{
log.fatal(e.getMessage());
return null;
}
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void SaveOrUpdateMessage(Message message)
{
try
{
Session mySession = sessionFactory.getCurrentSession();
mySession.save(message);
mySession.flush();
} catch (Exception e)
{
log.fatal(e.getMessage());
}
}
}
Here is my main class:
public static void main(String[] args)
{
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
MessageService mService = context.getBean(MessageService.class);
HelloWorld helloWorld = context.getBean(HelloWorld.class);
/**
* Date: 4/26/13 / 9:26 AM
* Comments:
*
* I added Log4J to the example.
*/
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
Message message = new Message();
message.setMessage(helloWorld.getMessage());
//
mService.SaveMessage(message);
helloWorld.setMessage("I am in Staten Island, New York");
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
}
}
Here is my DatabaseConfig:
public class DatabaseConfig
{
private static final Logger LOGGER = getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).
addScript("schema.sql").build();
return db;
}
#Bean
public DataSource hsqlDataSource() {
BasicDataSource ds = new BasicDataSource();
try {
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUsername("sa");
ds.setPassword("");
ds.setUrl("jdbc:hsqldb:mem:mydb");
}
catch (Exception e)
{
LOGGER.error(e.getMessage());
}
return ds;
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(hsqlDataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[]{"com.xxxxx.HelloSpringJavaBasedJavaConfig.model"});
try
{
factoryBean.afterPropertiesSet();
} catch (IOException e)
{
LOGGER.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
}
and here is what I am getting to the console:
Exception in thread "main" org.hibernate.AssertionFailure: null id in com.xxx.HelloSpringJavaBasedJavaConfig.model.Message entry (don't flush the Session after an exception occurs)
Here is my message model bean:
#Entity
#Table(name = "messages")
public class Message
{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
#Column(name = "message")
private String message;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
#Override
public String toString()
{
return "Message{" +
"id='" + id + '\'' +
", message='" + message + '\'' +
'}';
}
}
This relates to the version of hsql being used probably the version causing issue was 1.8 with hibernate 4. Need to use 2.2.9 instead
You can't use a String with #GenerateValue with the Strategy GenerationType.AUTO since it uses sequence generator and those can't be used with non-numerical values. You have to set it yourself. Use an Integer or Long if you want it to be generated for you.
Hibernate docs
Or use an id generator that uses string values
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
it was a version issues. I updated the versions and now everything works
I had the same issue after I upgraded hibernate to version 4.2.8 .Looking in the logs, I noticed that the sql query generated by hibernate tried to insert a record with a null primary key. The field was annotated just with: #Id #GeneratedValue
Upgrading hsqldb to version 2.2.9 made this disappear just like Denis said and I am very thankful to him for the reply.
It seems very likely that this issue is related to attempting to use a Session which has already signaled an error. As Sotirios mentioned, it is a bad idea to catch exceptions in your DAO, and if you do, it is critical that you re-throw them.
Normally, once you catch a Hibernate exception, you must roll back your transaction and close the session as the session state may no longer be valid (Hibernate core documentation).
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
Since you're using Spring, most of that doesn't apply to you, but the exception message you are seeing indicates that the actual cause of your problem probably was related to catching an exception and then continuing to use the same session.

Categories