I am new to r2dbc. I am trying to connect to MSSQL DB using R2DBC (non spring project) with reactor. It is not establishing the connection and also the data is not getting inserted into the table. I have tried by giving wrong table name as well, but there is no exception for it.
public Flux<MssqlResult> writetoDB() {
return createDBConnection().create()
.flatMapMany(c -> c.createStatement("INSERT INTO person (id, first_name, last_name) VALUES(#id, #firstname, #lastname)")
.bind("id", 1)
.bind("firstname", "Walter")
.bind("lastname", "White")
.execute()
.doFinally((st) -> c.close()))
.log();
}
private MssqlConnectionFactory createDBConnection() {
MssqlConnectionConfiguration configuration = MssqlConnectionConfiguration.builder()
.host("sample-host").username("testuser")
.password("testuser1").database("testDB").preferCursoredExecution(true).build();
MssqlConnectionFactory factory = new MssqlConnectionFactory(configuration);
return factory;
}
Kindly suggest what I am missing here.
Related
I'm trying to modify existing Java app (WildFly, Jboss, oracle) which currently working fine as using persistence-unit and EntityManager connect to Oracle database(using standalone.xml and persistence.xml). However, I need to create every time new connection to database for the user which calls new GET API Endpoint using credentials from the HttpHeaders. Currently, I'm creating new entitymanager object which session is commit, rollback nad close. Unfortunately time response for every call become higher and higher. There is warning about "PersistenceUnitUser" being already registered and memory usage constantly growing. So that is bad solution.
Is there any proper way to do it, which works witout any harms ?
P.S.
Currently app using standalone.xml and persistence.xml. And that is working fine. I'm calling java api endpoint using entity manager being connected as Admin user/pass but I need to create new connection using user/pass from the httpHeaders and call one sql statement to see proper results as ORACLE uses reserved word such us: 'user'. For instance : select * from table where create_usr = user. When done 'Main EntityManager will use data from it to continue some process.
Please see code example below :
#GET
#Path("/todo-list-enriched")
#Produces(MediaType.APPLICATION_JSON)
public Response getToDoListEnriched(#Context HttpHeaders httpHeaders, #QueryParam("skip") int elementNumber, #QueryParam("take") int pageSize, #QueryParam("orderby") String orderBy)
{
String userName = httpHeaders.getHeaderString(X_USER_NAME);
String userName = httpHeaders.getHeaderString(X_PASSWORD);
EntityManager entityManager = null;
try {
Map<String, String> persistenceMap = new HashMap<String, String>();
persistenceMap.put("hibernate.dialect","org.hibernate.dialect.Oracle8iDialect");
persistenceMap.put("hibernate.connection.username", asUserName);
persistenceMap.put("hibernate.connection.password", asPassword);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceUnitUser", persistenceMap);
entityManager = emf.createEntityManager();
if (!entityManager.getTransaction().isActive()) {
entityManager.getTransaction().begin();
}
-- Do some works as select, update, select
-- and after that
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().commit();
}
}
catch (Exception ex)
{
if (entityManager != null && entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
}
finally {
if (entityManager != null && entityManager.isOpen()) {
entityManager.close();
}
}
}
}
``
Best Regards
Marcin
You should define a connection pool and a datasource in the standalone.xml (cf. https://docs.wildfly.org/26.1/Admin_Guide.html#DataSource) and then use it in your persistence.xml and inject the EntitytManager in your rest service class (cf. https://docs.wildfly.org/26.1/Developer_Guide.html#entity-manager).
You may look at this example application: https://github.com/wildfly/quickstart/tree/main/todo-backend
I am trying to setup SQLITE as an in-memory database in my spring-boot application. But when i try to query the database then it gives me an error "No such table"
Can someone please recommend what am i doing wrong? I need to have SQLITE as a in memory only and we only use jdbc in our project.
Here is my code:
application.properties
spring.datasource.url=jdbc:sqlite:memory
spring.datasource.username=
spring.datasource.password=
spring.datasource.platform=sqlite
spring.datasource.driver-class-name=org.sqlite.JDBC
MyRepo.java
#Repository
public class MyRepo{
#Autowired
private NamedParameterJdbcTemplate namedJdbc;
public String getUserName() throws Exception{
String userName = null;
String sql = "SELECT username FROM emp WHERE username=:name";
MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("name", "tuser");
userName = this.namedJdbc.query(sql, paramSource, (rs) -> {
String name = null;
while (rs.next()) {
name = rs.getString("username").trim();
return name;
}
return null;
});
return userName;
}
}
UserDaoTest.java
#SpringBootTest
public class UserDaoTest {
#Autowired
private MyRepo rep;
#Test
public void testFindByname() throws Exception{
rep.getUserName();
}
}
I also have schema.sql and data.sql files under src/main/resources
schema.sql
DROP TABLE IF EXISTS emp;CREATE TABLE IF NOT EXISTS emp(username VARCHAR(20), empId BIGINT, PRIMARY KEY(empId) )
data.sql
INSERT INTO emp(username,empId) VALUES ('tuser',1001);
Exception that i am getting:
PreparedStatementCallback; uncategorized SQLException for SQL [SELECT username FROM Chats WHERE username=?]; SQL state [null]; error code [1]; [SQLITE_ERROR] SQL error or missing database (no such table: Chats)
well, I am shooting in the dark but looks like you need to add the schema for 'Chats' table as well to your schema.sql
https://sqlite.org/inmemorydb.html
The database ceases to exist as soon as the database connection is closed. Every :memory: database is distinct from every other. So, opening two database connections each with the filename ":memory:" will create two independent in-memory databases.
Your issue might be with spring boot opening multiple connections due to its connection pool configuration. If you're using hikari connection pool (default in newer spring boot versions), try adding these properties
spring.datasource.hikari.maximum-pool-size=1
spring.datasource.hikari.max-lifetime=0
I'm a C# developer and need to maintain an existing Java Service application developed using spring boot framework. The responsible developer left the company some time ago so I have no possibility to get some help...
So far I have no experience with Java and the used spring boot framework.
What I need to achive:
Check if used h2 Database is corrupted
If corrupted: Delete the database and create a new empty one
I guess I need to implement the check and recreation in the main entry point
public static void main(String[] args) {
SpringApplication.run(MessageServiceApplication.class, args);
}
As I know spring-boot and Hibernate creates the db automatically on startup if the database does not exists. So far so good. Now I need to check if the database is corrupted. I thought about executing a query on the database and if I get an exception I recreate the database.
The Database is a h2 file database.
Hopefully I can get some assistance.
Edit #1
I thought about implementing a utils class which gets called on startup:
public class H2DbUtils {
public boolean IsH2FileDatabaseCorrupted()
{
boolean isCorrupted = false;
// Implement Logic to determine if db is corrupted
return isCorrupted;
}
public boolean ReCreateH2DatabaseFile()
{
boolean reCreated = false;
// Implement Logic to recreate db
return reCreated;
}
}
Calling this class on startup
public static void main(String[] args) {
H2DbUtils h2DbUtils = new H2DbUtils();
if(h2DbUtils.IsH2FileDatabaseCorrupted()) {
h2DbUtils.ReCreateH2DatabaseFile();
}
SpringApplication.run(MessageServiceApplication.class, args);
}
Update 2018-03-20
Currently found the following solution to achive this:
#Configuration
#Component
public class DataSourceBean {
#Autowired
private Environment currentEnvironment;
private final Logger logInstance = LoggerFactory.getLogger(this.getClass());
#Bean
#Primary
public DataSource dataSource()
{
DataSource dataSource = null;
try
{
// We try to get the Meta Data out of the database.
// If this fails the database is corrupted or has an other problem
// All in all this means we need to delete the current database file
// to avoid further problems.
dataSource = this.getDataSource();
dataSource.getConnection().getMetaData();
return dataSource;
}
catch (Exception ex)
{
logInstance.error("The h2 database file '{}' seems to be corrupted! Error: {}",
currentEnvironment.getProperty("dataBaseFile"),
ex.getMessage());
// dataBaseFile=./db/mydatabase.db
String databaseFilePath = String.format("%s.%s", currentEnvironment.getProperty("dataBaseFile"), "h2.db");
databaseFilePath = databaseFilePath.replace("/", "\\");
File databaseFile = new File(databaseFilePath);
if (databaseFile.exists()) {
File parentDirectory = new File(databaseFile.getParent());
if (parentDirectory.isDirectory()) {
try {
FileUtils.deleteDirectory(parentDirectory);
} catch (Exception fex) {
logInstance.error("Error occurred deleting the folder {}. Error: {}",
parentDirectory.getAbsolutePath(),
fex.getMessage());
}
}
}
dataSource = this.getDataSource();
}
finally {
return dataSource;
}
}
#ConfigurationProperties(prefix = "spring.datasource")
private DataSource getDataSource() {
return DataSourceBuilder.create()
.url(currentEnvironment.getProperty("spring.datasource.url"))
.driverClassName(currentEnvironment.getProperty("spring.datasource.driverClassName"))
.username(currentEnvironment.getProperty("spring.datasource.username"))
.password(currentEnvironment.getProperty("spring.datasource.password"))
.build();
}
It is possible to overwrite the DataSource bean and check database files
#Bean
#Primary // this will override the datasource autoconfiguration and use your own everywhere
public DataSource dataSource() {
// Open Connection
// Check Database
// Close Connection
// IF File corrupted delete files
// create regular data source
}
I've tried to add several listeners to the spring boot application, for example:
SpringApplication springApplication = new SpringApplication(testApplication.class);
springApplication.addListeners(new FailedEvent(testApplication.class));
SpringApplication.run(testApplication.class, args);
But I never get to one of this listeners in the startup of the spring application. As SpringApplication.run seems to initzialise the whole spring context it is also not possible to inject or get the configuration environment to get the connection string as the application stops within SpringApplication.run as the db is corrupted.
I assume that spring tries to initzialise hibernate and so on and fails to create a database connection as the db is corrupted
org.h2.jdbc.JdbcSQLException: Allgemeiner Fehler: "java.lang.RuntimeException: rowcount remaining=2 SYS"
General error: "java.lang.RuntimeException: rowcount remaining=2 SYS" [50000-196]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.196.jar:1.4.196]
at org.h2.message.DbException.get(DbException.java:168) ~[h2-1.4.196.jar:1.4.196]
at org.h2.message.DbException.convert(DbException.java:295) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Database.openDatabase(Database.java:307) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Database.<init>(Database.java:270) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Engine.openSession(Engine.java:64) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Engine.openSession(Engine.java:176) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Engine.createSessionAndValidate(Engine.java:154) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Engine.createSession(Engine.java:137) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.Engine.createSession(Engine.java:27) ~[h2-1.4.196.jar:1.4.196]
at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:354) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:116) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:100) ~[h2-1.4.196.jar:1.4.196]
at org.h2.Driver.connect(Driver.java:69) ~[h2-1.4.196.jar:1.4.196]
This happens within the SpringApplicatio.run context. Before I found no chance to check if the db is corrupted and if so delete the database.
First let me describe the problem I am facing.
We are developing a web service using Jersey and MySQL at back-end. It's simple that user can view or edit data via Restful API, and the service will load/save data to or from MySQL database.
I created several stored procedure at MySQL server, like SelectAnswer and UpdateAnswer. In the program, we use standard JDBC and MySQL connector to connect to the database.
Firstly I created stored procedure per connection. Like each time the program start a new connection to the database, it creates a bunch of Stored Procedure.
public enum JDBCDataSource {
INSTANCE;
private BasicDataSource dataSource = new BasicDataSource();
private Connection conn;
private CallableStatement answerUpsert;
private JDBCDataSource(){
initConnection();
try{
conn = dataSource.getConnection();
answerUpsert = prepareSP(answerUpsert,"{call upsert_answer(?, ?, ?)}");
} catch (SQLException e) {
e.printStackTrace();
}
}
And the program reuse the stored procedure for each time calling:
private void executeUpsert(String app, String id, String content)
throws SQLException{
try {
CallableStatement callableStatement = JDBCDataSource.INSTANCE.getUpsert();
callableStatement.setString(1,app);
callableStatement.setInt(2,Integer.valueOf(id));
callableStatement.setString(3,content);
callableStatement.execute();
} catch (NumberFormatException e) {
e.printStackTrace();
} finally {
callableStatement.clearParameters();
}
}
So each time calling the procedure, the function set its own parameters, and execute the SP, and finally clear the parameters.
But it is not thread safe, if the user post two request and one request is trying to set parameters, and another to clear parameters, it will cause an exception.
So for stored procedure in MySQL, should I create it per transaction or per connection in order to keep it thread safe? Maybe my understanding of SP and MySQL is not correct, or maybe it is design problem. Please share your though for my question.
I have a requirement to insert a list of POJO to be inserted into the database. I have a stored procedure which performs one insert at a time. In the current implementation, I have a splitter that splits on the POJO and passes that payload to the stored proc outbound gateway to call my stored procedure.
In a real time scenario my list size could be as big as 500K. So Is there a way for a better implementation? Is there a way to perform a batch insert in SI flow?
Thanks
I wrote mentioned custom outbound channel adapter, which uses spring-jdbc batch ability:
public class ArrayListSqlBatchOBCA {
private final static Logger log = LoggerFactory.getLogger(ArrayListSqlBatchOBCA.class);
private NamedParameterJdbcTemplate template;
private String sql;
public void process(Message<? extends ArrayList<?>> message) throws Exception {
try {
ArrayList<?> list = (ArrayList<?>) message.getPayload();
SqlParameterSource[] batchArgs = new SqlParameterSource[list.size()];
for (int i = 0; i < list.size(); i++) {
batchArgs[i] = new BeanPropertySqlParameterSource(list.get(i));
}
template.batchUpdate(sql, batchArgs);
}
catch (Exception e) {
log.error("Exception while processing message", e);
throw e;
}
}
}
This is how I use it (XML config):
<channel id="cc"/>
<outbound-channel-adapter channel="cc" method="process">
<beans:bean class="mypackage.ArrayListSqlBatchOBCA">
<beans:property name="template" ref="jdbcTemplate"/>
<beans:property name="sql"
value="INSERT INTO test (field1,field2,field3)
VALUES (:f1,:f2,:f3)"/>
</beans:bean>
</outbound-channel-adapter>
Further, I added ?reWriteBatchedInserts=true to jdbc url of my datasource (I use postgres JDBC driver).
Documentation about the option:
reWriteBatchedInserts - Enable optimization to rewrite and collapse compatible INSERT statements that are batched. If enabled, pgjdbc rewrites batch of insert into ... values(?, ?) into insert into ... values(?, ?), (?, ?), ...
This way, using a small custom code, I insert multiple rows at once from input collection.