R2DBC queries inside one reactive stream - spring-webflux

After every test, DB needs to be erased. This way the DELETE queries are not called.
#SpringBootTest(
webEnvironment = RANDOM_PORT,
properties = {"spring.main.allow-bean-definition-overriding=true"}
)
public abstract class BaseTest {
#Autowired
private DatabaseClient client;
#AfterEach
public void eraseDb() {
client.sql("select table_name from information_schema.tables "
+ "where table_catalog='aggregator' and table_schema='public' and table_name not like 'databasechange%'")
.map(row -> row.get("table_name"))
.all()
.doOnNext(tableName -> client.sql(format("DELETE FROM %s", tableName)).then().subscribe())
.subscribe();
}
}
This way it works:
client.sql("select table_name from information_schema.tables "
+ "where table_catalog='aggregator' and table_schema='public' and table_name not like 'databasechange%'")
.map(row -> row.get("table_name"))
.all().collectList().block()
.forEach(tableName -> client.sql(format("DELETE FROM %s", tableName)).then().subscribe());
Can someone explain why the first method is not working.

Related

How to check if a database entry exists using HQL description

The JpaRepository allows for executing some SQL queries without having to specify these specifically. For example, it is possible to execute the following method: myRepository.existsById(id).
I would like to execute this method, but instead of checking for the existence of the id, I would like to check for a different property, which I call employeeId. I am aware that I will have to specify this query; this is what I have tried:
#Query("SELECT 1 FROM MyTable t WHERE t.employeeId = ?1")
Integer existsByEmployeeId(Long id);
I am calling this method just before executing a DELETE query:
public Long deleteEntryByEmployeeId(Long id) {
if (myRepository.existsByEmployeeId(id) == 1) {
myRepository.deleteEntryByEmployeeId(id);
return id;
}
return null;
}
This doesn't really work as expected though, as I am returned the error:
Cannot invoke "java.lang.Integer.intValue()" because the return value of "myRepository.existsByEmployeeId(java.lang.Long)" is null
I understand that myRepository.existsByEmployeeId(id) returns null. This seems to be the case if there is no entry in myTable for the specified id. Anyways, I am looking for a smooth solution to this problem. Can somebody help?
There are multiple approaches.
You want to stick with declarative #Query annotation.
#Query("SELECT CASE WHEN count(t) = 1 THEN TRUE ELSE FALSE END FROM MyTable t WHERE t.employeeId = ?1")
Integer existsByEmployeeId(Long id);
It's OK to implement custom functionality.
public interface CustomEmployeeRepository {
boolean existsByEmployeeId(Long id);
}
#Repository
#Transactional(readOnly = true)
class CustomEmployeeRepository implements CustomEmployeeRepository {
#PersistenceContext
private EntityManager em;
#Override
boolean existsByEmployeeId(Long id) {
List list = em.createQuery("SELECT 1 FROM MyTable t WHERE t.employeeId = :id")
.setParameter("id", id)
.getResultList();
return list.size() == 1;
}
}
// inject EmployeeRepository and call existsByEmployeeId as usual
public interface EmployeeRepository extends JpaRepository<Employee, Long>, CustomEmployeeRepository {
}

ArchUnit case insensitive name matching

I'm trying to setup tests with Arch Unit to test my naming conventions, following the official examples.
It seems ArchUnit's naming assertions are case sensitive, which is bothering me.
I want to test that no classes in the package domain.service contains the word service.
Given a class domain.service.FileSystemService:
This test passes:
#ArchTest
val domain_service_should_not_have_names_containing_service: ArchRule =
noClasses()
.that().resideInAPackage("..domain.service..")
.should().haveSimpleNameContaining("service")
This test fails:
#ArchTest
val domain_service_should_not_have_names_containing_service: ArchRule =
noClasses()
.that().resideInAPackage("..domain.service..")
.should().haveSimpleNameContaining("Service")
Am I missing something? Is there a way to make ArchUnit's comparisons case insensitive?
If you don't want to use haveNameMatching as proposed in the comments you can also create your own ArchConditions.
public static ArchCondition<JavaClass> containName(String namePart) {
return new NameContains(namePart);
}
private static class NameContains extends ArchCondition<JavaClass> {
private String namePart;
NameContains(String namePart) {
super("contain '" + namePart + "' in the name");
this.namePart = namePart;
}
#Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean containsName = javaClass.getSimpleName().toLowerCase().contains(namePart.toLowerCase());
String message;
if (containsName) {
message = createCheckMessage(javaClass, "contains '" + namePart + "' in the name");
} else {
message = createCheckMessage(javaClass, "does not contain '" + namePart + "' in the name");
}
events.add(new SimpleConditionEvent(javaClass, containsName, message));
}
}
// taken from com.tngtech.archunit.lang.conditions.ArchConditions
public static <T extends HasDescription & HasSourceCodeLocation> String createCheckMessage(T object,
String message) {
return object.getDescription() + " " + message + " in " + object.getSourceCodeLocation();
}
You can use it like this:
rules.add(noClasses().that().resideInAPackage("..domain.service..").should(containName("Service")));

How to Create Local Sqlite Data Table on Xamarin and Check it with Device Monitor?

Have My DatabaseHelper Class
public class DatabaseHelper : SQLiteOpenHelper
And Methods inside like
private static readonly String CREATE_USER_TABLE = "CREATE TABLE "
+ TABLE_USERS + "(" + USER_ID + " TEXT PRIMARY KEY," + USER_PASS
+ " TEXT," + USER_NAME + " TEXT," + USER_SURNAME + " TEXT,"
+ KEY_CREATED_AT + " DATETIME" + ")";
Here my DatabaseHelper class Constructure and onCreate() onUpgrade() methods
public DatabaseHelper(Context context): base(context,DATABASE_NAME,null,DATABASE_VERSION){}
public override void OnCreate (SQLiteDatabase db)
{
db.ExecSQL (CREATE_USER_TABLE);
db.ExecSQL (CREATE_RECORDS_TABLE);
db.ExecSQL (CREATE_COMPANY_TABLE);
db.ExecSQL (CREATE_COMPANY_LIST_TABLE);
db.ExecSQL (CREATE_NOTIUPLIST_TABLE);
db.ExecSQL (CREATE_NOTIFICATION_DELETE_LIST_TABLE);
db.ExecSQL (CREATE_MYNOTIFICATION_LIST_TABLE);
}
public override void OnUpgrade (SQLiteDatabase db, int oldVersion, int newVersion)
{
throw new NotImplementedException ();
}
And I call it from mainActivity
DatabaseHelper db = new DatabaseHelper (Application.Context);
Looking through Android Device Monitor data-->data-->my package name--> but dont see any created table Where I am missing to do something?
I have also added SQLite -net PCL package
You can't view your database on an actual device, unless that device is rooted. All of the examples you are seeing that are explaining how to find your database on the file system are assuming you're using an emulator.

inner-join query in Hibernate

I want to run a query with Hibernate, but I don't know what I'm donig wrong that I get errors.
I tried following
public List<String> findCourseForStudent(String pnr) {
factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
String sql = "select Course.name from Course inner join CourseMaterial "
+ "on CourseMaterial.course_id = Course.id inner join CourseParticipantship "
+ "on CourseMaterial.id = CourseParticipantship.courseMaterial_id inner join Student "
+ "on Student.id=CourseParticipantship.student_id where Student.personalNumber='" + pnr + "'";
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
query.setParameter("pnr", pnr);
List data = query.list();
tx.commit();
return data;
} catch(HibernateException e) {
if(tx != null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return null;
}
but I get the following error:
net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: DefaultConfigurationSource [ ehcache.xml or ehcache-failsafe.xml ]
I've searched for the error and tried some of the solutions, but none of them worked for me, so I tried to use HQL instead of using sql directly, but still no success :(
I don't actually know how to use INNER_JOIN in DetachedCriteria otherwise I think using DetachedCriteria I won't get any errors because I tried to use it for simpler queries and it worked fine.
Following is an example for a simple query that works fine, but I don't know how to wirte it for innerjoin queries
public List<Student> findForCourse(Integer integer) {
DetachedCriteria criteria = DetachedCriteria.forClass(Student.class);
criteria.add(Restrictions.eq("course.id", integer));
criteria.add(Restrictions.ne("active", Boolean.FALSE));
return getHibernateTemplate().findByCriteria(criteria);
}
Finally, I found out how to write my query in hibernate
public List<String> findCourseForStudent(String pnr) {
return jdbcTemplate.query(
"select Course.name from Course inner join CourseMaterial "
+ "on CourseMaterial.course_id = Course.id inner join CourseParticipantship "
+ "on CourseMaterial.id = CourseParticipantship.courseMaterial_id inner join thd.dbo.Student "
+ "on Student.id=CourseParticipantship.student_id where Student.personalNumber= ?", new Object[] { pnr }, new RowMapper() {
public Object mapRow(ResultSet rs, int row) throws SQLException {
return rs;
}
});
}

Spring JDBC and Java 8 - JDBCTemplate: retrieving SQL statement and parameters for debugging

I am using Spring JDBC and some nice Java 8 lambda-syntax to execute queries with the JDBCTemplate.
The reason for choosing Springs JDBCTemplate, is the implicit resource-handling that Spring-jdbc offers (I do NOT want a ORM framework for my simple usecase's).
My problem is that I want to debug the whole SQL statements with their parameters. Spring prints the SQL by default but not the parameters. Therefor I have subclassed the JDBCTemplate and overridden a query-method.
An example usage of the JDBCTemplate:
public List<Product> getProductsByModel(String modelName) {
List<Product> productList = jdbcTemplate.query(
"select * from product p, productmodel m " +
"where p.modelId = m.id " +
"and m.name = ?",
(rs, rowNum) -> new Product(
rs.getInt("id"),
rs.getString("stc_number"),
rs.getString("version"),
getModelById(rs.getInt("modelId")), // method not shown
rs.getString("displayName"),
rs.getString("imageUrl")
),
modelName);
return productList;
}
To get hold of the parameters I have, as mentioned, overridden the JDBCTemplate class. By doing a cast and using reflection I get the Object[] field with the parameters from an instance of ArgumentPreparedStatementSetter.
I suspect this implementation could potentially be dangerous, as the actual implementation of the PreparedStatementSetter may not always be ArgumentPreparedStatementSetter (Yes I should do an instanceOf check). Also, the reflection code may not be as elegant, but that is besides the point now though :).
Here's my custom implementation:
public class CustomJdbcTemplate extends JdbcTemplate {
private static final Logger log = LoggerFactory.getLogger(CustomJdbcTemplate.class);
public CustomJdbcTemplate(DataSource dataSource) {
super(dataSource);
}
public <T> T query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {
if(log.isDebugEnabled()) {
ArgumentPreparedStatementSetter aps = (ArgumentPreparedStatementSetter) pss;
try {
Field args = aps.getClass().getDeclaredField("args");
args.setAccessible(true);
Object[] parameters = (Object[]) args.get(aps);
log.debug("Parameters for SQL query: " + Arrays.toString(parameters));
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new GenericException(e.toString(), e);
}
}
return super.query(psc, pss, rse);
}
}
So, when I execute the log.debug(...) statement I would also like to have the original SQL query logged (same line). Has anyone done something similar or are there any better suggestions as to how this can be achieved?
I do quite a few queries using this CustomJDBCTemplate and all my tests run, so I think it may be an acceptable solution of for most debug purposes.
Kind regards,
Thomas
I found a way to get the SQL-statement, so I will answer my own question :)
The PreparedStatementCreator has the following implementation:
private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider
So the SqlProvider has a getSql() method which does exactly what I need.
Posting the "improved" CustomJdbcTemplate class if anyone ever should need to do the same :)
public class CustomJdbcTemplate extends JdbcTemplate {
private static final Logger log = LoggerFactory.getLogger(CustomJdbcTemplate.class);
public CustomJdbcTemplate(DataSource dataSource) {
super(dataSource);
}
public <T> T query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {
if(log.isDebugEnabled()) {
if(pss instanceof ArgumentPreparedStatementSetter) {
ArgumentPreparedStatementSetter aps = (ArgumentPreparedStatementSetter) pss;
try {
Field args = aps.getClass().getDeclaredField("args");
args.setAccessible(true);
Object[] parameters = (Object[]) args.get(aps);
log.debug("SQL query: [{}]\tParams: {} ", getSql(psc), Arrays.toString(parameters));
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new GenericException(e.toString(), e);
}
}
}
return super.query(psc, pss, rse);
}
private static String getSql(Object sqlProvider) { // this code is also found in the JDBCTemplate class
if (sqlProvider instanceof SqlProvider) {
return ((SqlProvider) sqlProvider).getSql();
}
else {
return null;
}
}
}