This question already has answers here:
Spring: How to inject a value to static field?
(5 answers)
Closed 4 years ago.
I am a beginner with Spring and I am working on a spring-boot project with Jooq.
I created an application properties
which loads the database access settings,
it's working, but I need a connection class that returns a connection object for me to use when needed.
It would be better if this class had a static getConnection method. I wrote the class as #Component and I put my
#Value but all these attributes are null when I try to use them.
Is there another way to do this?
Thank you so much.
#Component
public class ConnectionFactory {
#Value("${bws.codigo.escola}")
static private String codigoEscola;
#Value("${bws.driver.connection}")
static private String driver;
#Value("${bws.database.name}")
static private String nameDb;
#Value("${bws.database.user}")
static private String user;
#Value("${bws.database.password}")
static private String password;
#Value("${bws.database.server}")
static private String server;
#Value("${bws.string.connection}")
static private String firstUrlPart;
#Value("${bws.string.connectionLast}")
static private String lastPartUrl;
#Value("${bws.database.port}")
static private String port;
static String strConnection = firstUrlPart+server+":"+port+"/"+nameDb+lastPartUrl;
public static Connection getConnection() throws SQLException {
try {
Class.forName(driver);
return DriverManager.getConnection(strConnection,
user,password);
} catch (ClassNotFoundException e) {
throw new SQLException(e.getMessage());
}
}
}
that way I would use it in other classes
try (Connection con = ConnectionFactory.getConnection()) {
DSLContext ctx = DSL.using(con, SQLDialect.MYSQL);
int count = ctx.selectCount()
.from(ALUNO)
.fetchOne(0, int.class);
return count;
}
You could, of course, go ahead and inject all the values required to create a JDBC connection for each of your query on an ad-hoc basis. Or, much better, you inject a pre-configured DSLContext singleton instance that wraps the JDBC connection through a DataSource (ideally implemented by a connection pool).
The Spring Boot manual illustrates how this can be done:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-jooq
A more complete example can be seen here:
https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-spring-boot-example
The goal would be to have:
class SomeClass {
#Autowired
DSLContext ctx;
public int countAluno() {
return ctx.selectCount()
.from(ALUNO)
.fetchOne(0, int.class);
}
}
Related
I am using TestContainer to run RabbitMQ instance in order to use it in my integration tests.
I create a Junit 5 extension class that implement the BeforeAllCallback interface to run the container only once before my tests, to connect to the container i need to retrieve the mapped port that is exposed in my host machine, so i am wandering if there is any solution in order to access the extension class field from my integration test class.
The Extension
public class RmqExtension implements BeforeAllCallback {
private static final String DEFAULT_USER = "guest";
private static final String DEFAULT_PASS = "guest";
public static final int RABBIT_HTTP_API_PORT = 15672;
private static final String RABBIT_MQ_IMAGE_NAME = "rmqImage";
private static final String RABBIT_MQ_OVERVIEW_PATH = "/api/overview";
private static final GenericContainer rabbitMqContainer = new GenericContainer(DockerImageName.parse(RABBIT_MQ_IMAGE_NAME))
.withExposedPorts(RABBIT_HTTP_API_PORT)
.waitingFor(Wait.forHttp(RABBIT_MQ_OVERVIEW_PATH).withBasicCredentials(DEFAULT_USER, DEFAULT_PASS).forStatusCode(HttpStatus.SC_OK));
#Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
rabbitMqContainer.start();
}
}
My test Class
#ExtendWith(RmqExtension.class)
class RabbitMqIT {
private int myPort;
#Test
void myTest(){
// What i need to do
myPort = rabbitMqContainer.getMappedPort(15672);
}
}
I am unsure what is the most elegant JUnit-Jupiter-idiomatic way to do this, but if there is only 1 instance of the container per JVM process, you could either use a public static field or save it System Properties.
Also, see the Singleton Container Pattern for another example of how to do this without JUnit:
https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers
I want to read my database property values from the application.property file but not being able to.
I tried reading the properties from custom properties file but it didnt work. I don't want to hard code the properties as it will will be different in different servers.
#Configuration
#PropertySource("application.properties")
public class DatabaseUtils {
#Value("${mysql.drive}")
private static String MY_SQL_DRIVER;
#Value("${mysql.url}")
private static String MY_SQL_URL;
#Value("${mysql.username}")
private static String DATABASE_USERNAME;
#Value("${mysql.password}")
private static String DATABASE_PASSWORD;
public DatabaseUtils () {
}
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
public static ResultSet executeDBQuery(String query) {
try {
Class.forName(MY_SQL_DRIVER);
Connection connection = DriverManager.getConnection(MY_SQL_URL, DATABASE_USERNAME, DATABASE_PASSWORD);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
return resultSet;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
(properties file)
# database
databaseDriver=com.mysql.jdbc.Driver
databaseUrl=jdbc:mysql://localhost:3306/school
databaseUsername=root
databasePassword=root
I am new to spring-boot so getting a little confused as to why its not working, read a lot of online sources and tried them but didnt work, not sure what I am missing. Any help will be appreciated.
The properties in your property file is different from what you are giving in #Value.for eg. your property key is databaseDriver but you are giving mysql.drive in your code.
#Value("${mysql.drive}")
private static String MY_SQL_DRIVER;
But it should be
#Value("${databaseDriver}")
private static String MY_SQL_DRIVER;
basically what properties you define in the properties file should be referred in the application right. Here you are defining property as databaseDriver.
databaseDriver=com.mysql.jdbc.Driver
Then in code also you should ask for "databaseDriver"
#Value("${databaseDriver}")
private static String MY_SQL_DRIVER;
I have a singleton class that I created that is used as an adapter to our Influx database. It basically looks like:
public class InfluxDBAdapter {
// some private static final query Strings
private static InfluxDBAdapter adapter = null;
private static InfluxDB influxDB;
private InfluxDBAdapter() {}
public static InfluxDBAdapter getInstance() {
if (adapter == null) {
adapter = new InfluxDBAdapter();
influxDB = InfluxDBFactory.connect(URL, USERNAME, PWD);
influxDB.query(new Query(CREATE_DB, DB_NAME));
influxDB.setLogLevel(InfluxDB.LogLevel.BASIC);
influxDB.setDatabase(DB_NAME);
}
return adapter;
}
// some more methods to utilize the database
}
and then in another class, I use it like so:
#Service
public class SomeService {
private InfluxDBAdapter adapter;
public SomeService() {}
#PostConstruct
public void init() {
adapter = InfluxDBAdapter.getInstance();
}
}
And so this works, but I'm in the midst of refactoring my code and I wanted to know if was possible to simply autowire my InfluxDBAdapter class vs what I'm currently doing and still achieve the same result?
Create an #Configuration class which both constructs the InfluxDB as well as your adapter. With this you could even make use of the Spring Boot properties support.
#Configuration
public class InfluxDBConfiguration {
#Bean
public InfluxDB influxDB() {
InfluxDB influxDB = InfluxDBFactory.connect(URL, USERNAME, PWD);
influxDB.query(new Query(CREATE_DB, DB_NAME));
influxDB.setLogLevel(InfluxDB.LogLevel.BASIC);
influxDB.setDatabase(DB_NAME);
return influxDB;
}
#Bean
public InfluxDBAdapter influxDBAdapter(InfluxDB influxDB) {
return new InfluxDBAdapter(influxDB);
}
}
Now your InfluxDBAdapter needs a constructor (for dependency injection) retrieving the InfluxDB.
public class InfluxDBAdapter {
// some private static final query Strings
private InfluxDB influxDB;
InfluxDBAdapter(InfluxDB influxDB) {
this.influxDB=influxDB;
}
// some more methods to utilize the database
}
Make sure that the InfluXDBConfiguration and InfluxDBAdapter are in the same package so that the default visible constructor can be called (default visible to prevent, easy, outside instantiation).
In the InflxuDBConfiguration you could remove the static fields containing the hardcoded username etc. and replace it with access to either the Environment or use an #ConfigurationProperties annotated class to work with type safe properties.
#ConfigurationProperties(prefix="influxdb")
#Component
public class InfluxDBProperties {
private String url = "default-url";
private String dbName = "default-dbname";
private String username = "default-user";
private String password = "default-pwd";
// Other properties of your liking;
// getters & setters
}
Now with this InfluxDBProperties you could add influx.url=http://whatever to your application.properties or profile specific one and have it externally configurable. You can inject it into the influxDB method to retrieve the properties from.
#Bean
public InfluxDB influxDB(InfluxDBProperties props) {
InfluxDB influxDB = InfluxDBFactory.connect(props.getUrl(), props.getUsername(), props.getPassword());
influxDB.query(new Query(CREATE_DB, props.getDbName()));
influxDB.setLogLevel(InfluxDB.LogLevel.BASIC);
influxDB.setDatabase(props.getDbName());
return influxDB;
}
No more statics, configurable for every environment.
Yes, this should work. Spring can invoke private constructors so there shouldn't be any issue.
But why would you want to do this? The singleton pattern goes against the basic tenant of dependency injection. If you want a singleton InfluxDBAdapter bean, just make it a singleton bean.
I would recommend adding a configuration class, which could look something like
#Configuration
public class InfluxDBConfig {
// constants omitted...
#Bean
public InfluxDB influxDB() {
final InfluxDB influxDB = InfluxDB(URL, USERNAME, PWD);
influxDB.query(new Query(CREATE_DB, DB_NAME));
influxDB.setLogLevel(InfluxDB.LogLevel.BASIC);
influxDB.setDatabase(DB_NAME);
return influxDB;
}
}
You can then annotate InfluxDBAdapter with #Component since the InfluxDB instance can be injected. Modify the constructors of the InfluxDB and InfluxDBAdapter classes accordingly, of course.
Some of these constants can probably be provided through configuration properties so that your configuration logic isn't mangled with your business logic.
I'm currently using a pool connection(Hikari) and an abstract factory pattern to implement my MySQL queries in Java like this:
MySqlFactoryDAO.java
public class MySqlFactoryDAO extends FactoryDAO {
private static HikariDataSource connPool_;
public static Connection createConnection() throws SQLException {
if (connPool_ == null) {
// Load database configuration
PropertiesFile props = FactoryConfig.getConfig().getDatabaseProperties();
connPool_ = new HikariDataSource();
connPool_.setJdbcUrl(props.getString(Params.DB_URL,""));
connPool_.setUsername(props.getString(Params.DB_USER,"root"));
connPool_.setPassword(props.getString(Params.DB_PASSWORD,"root"));
}
return connPool_.getConnection();
}
//-------------------------------------------------------------------------
public ProductDAO getProductDAO() {
return new ProductMySQLFactoryDAO();
}
}
ProductMySQLFactoryDAO.java
public class ProductMySQLFactoryDAO implements ProductDAO {
public int insertProduct(String name) {
...
Connection conn = MySqlFactoryDAO.createConnection();
...
}
}
I was wondering if this code is thread safe and I think that there is a problem at the time of initialization of coonPool_. I have read things like "Initialization-on-demand_holder_idiom" in wikipedia but I am not sure about it. Does anyone have a better implementation of this to solve this problem or just a better new one?
No, it's not thread-safe. Two threads might call createConnection() concurrently, both see the pool as null, and both create a new DataSource.
The method needs to be synchronized. Or the pool must be created at class initializing time:
private static final HikariDataSource DATA_SOURCE = createDataSource();
Your connPool_ field should be private, too.
I have a question about closing resources (AutoCloseable or not) that are static members of a Java class. In my particular situation I have a class that manages connections to a MongoDB instance using a static MongoClient instance variable. How would I ensure that this client is closed as the documentation recommends when my application terminates? I am using this class as part of the back end to a Java webapp which is run in a container (Tomcat 7). I could not override the Object's finalize() method to close the client because that is called on an instance of the class and would have no effect on static members correct? Here is my example code:
public class MyManager {
//This needs to be closed when the application terminates
private static MongoClient CLIENT;
static {
...
CLIENT = new MongoClient("localhost", 27017);
...
}
public static DB getSomeDB(String dbName) {
return CLIENT.getDB(dbName);
}
//more factory methods
...
//Would this work?
#Override
protected void finalize() throws Throwable {
CLIENT.close();
}
}
Can someone tell me how to best handle this situation and in general with resources such as a database connection or JDBC driver? Thanks!
We are using Spring and simply create a bean which calls the close() method once it's being destroyed:
#Bean(name = "mongoClient", destroyMethod = "close")
public MongoClient mongoClient() throws MongoException, UnknownHostException {
...