I am looking for a SQL utility library that allows me to do things like escaping strings for prefix search using LIKE or programmatically writing the WHERE part of the query by adding terms (with placeholder support).
Projects like Quaere, LIQUidFORM, jaQu, JEQUEL (Java Embedded QUEry Language) all offer a fluent interface to write SQL statements and might be what you're looking for. For example, with JEQUEL:
public void testParameterExample() {
final Sql sql = select(ARTICLE.NAME, ARTICLE.ARTICLE_NO)
.from(ARTICLE)
.where(ARTICLE.OID.in(named("article_oid"))).toSql();
assertEquals("select ARTICLE.NAME, ARTICLE.ARTICLE_NO from ARTICLE where ARTICLE.OID in (:article_oid)", sql.toString());
final Collection<String> articleDesc = sql.executeOn(dataSource)
.withParams("article_oid", Arrays.asList(10, 11, 12))
.mapBeans(new BeanRowMapper<ArticleBean, String>() {
public String mapBean(final ArticleBean bean) {
return bean.getArticleNo() + "/" + bean.getName();
}
});
assertEquals(1, articleDesc.size());
assertEquals("12345/Foobar", articleDesc.iterator().next());
}
More of them at the bottom of the jaQu webpage.
A typical ORM framework like hibernate or JPA provides this out of the box.
e.g in hibernate .
from Document doc fetch all properties where lower(doc.name) like 'cats%'
will return Document object where the nqme start with cats.
For parameter queries :
Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
q.setProperties(fooBean); // fooBean has getName() and getSize()
List foos = q.list();
It will also save you from a lot of boilerplate to create all the JDBC objects needed and all the error handling.
If you need to stay close to SQL, give iBatis a careful look.
If you're still thinking about SQL in these low-level terms, I'd say you aren't being object-oriented enough.
Think about your problems in terms of objects. You're still too mired in the primitive level.
Consider better abstractions for persistence on top of your model objects: the DAO pattern, Spring JDBC, iBatis, ORM tools like Hibernate, etc.
Related
I'm building REST API connected to ORACLE 11G DB. API sends data to Android client using JSON. To get data I'm using JpaRepository, and #Query annotations.
I want to provide data for charts: number of contracts in years.
I have native SQL query:
select aa.ROK, count(aa.NUMER_UMOWY)
from (select distinct NUMER_UMOWY, ROK from AGR_EFEKTY) aa
group by aa.ROK order by aa.ROK
Result of query using SQL Developer look like this:
I tried to get result using native query:
But result is always like this:
or error depending what I try.
Is it possible to obtain list of count() results using #Query?
If not, what should I use?
Thanks in advance :-)
I think What you are trying to use here is spring data projection.
As mentioned in the reference doc:
Spring Data query methods usually return one or multiple instances of
the aggregate root managed by the repository. However, it might
sometimes be desirable to create projections based on certain
attributes of those types. Spring Data allows modeling dedicated
return types, to more selectively retrieve partial views of the
managed aggregates.
and particularly closed projection where all accessor methods match the target attributes. In your case the count is not an attribute of your aggregate.
To perform what you want you can use constructor as follow :
class ContractsDto{
private String rok;
private int count;
public ContractsDto(String rok, int count) {
this.rok=rok;
this.count =count;
}
// getters
}
The query will be:
#Query(value = "select new ContractsDto(aa.rok , /*count */) from fromClause")
List<ContractsDto> getContractsPerYear();
I use spring data JPA. I need in my repository request to load only collection of concrete properties colors:
#Query(value = "SELECT cd.color FROM CalendarDetails cd where cd.userCalendar.userId = :userId")
List<String> findCalendarColorsByUserWithDuplicates(#Param("userId") Long userId);
Provided solution works correctly.
I want simplify it using spring approach to load collection of the repository objects I'd use (repository public interface CalendarDetailsRepository extends JpaRepository<CalendarDetails, Long>):
List<CalendarDetails> findByUserCalendarUserId(#Param("userId") Long userId);
But I need collection of colors! Trying
List<String> findColorByUserCalendarUserId(Long userId);
I get collection of CalendarDetails
Is it possible to improve my last request following spring data approaches to load list of colors?
You can try special Projection mechanisms that Spring Data provides. It will allow you not only to optimize your queries but also to make it with pure java without using #Query.
There are a lot of ways to
make it, but I would recommend the following.
You add an interface that contains getters for the properties that you need to take from entity:
public interface ColorOnly {
String getColor();
}
Then you return the list of this interface' objects:
List<ColorOnly> findColorByUserCalendarUserId(Long userId);
To use the colours from the interface, you just invoke getColor method. You may consider simplifying it with Java 8 streams and map conversions. BTW, this one will only query colour. No other fields will be included into the query Hibernate produces.
Try to add All
findAllByUserCalendarUserId(Long userId);
BTW, IntelliJ IDEA provide very deep support of JPA repositories, so it's prevent a lot of possible issues when you create queries like this one
I've just started integrating QueryDSL into a Spring Boot project and I'm looking for a way to build a query out of an existing entity bean. Using #ModelAttribute it's nice and easy to pass in an entity via a GET request from the controller as long as the parameters align with the bean:
public Page<Company> getLogins(#ModelAttribute Company company, Pageable pageable, #RequestParam(value = "page", required = false) String pageNumber){
return companyService.findbyParameters(company,pageNumber);
}
And in the service class, I can use the BooleanBuilder to build up a query:
public Page<Company> findbyParameters(Company companySearch,String pageNumber){
QCompany company = QCompany.company;
BooleanBuilder builder = new BooleanBuilder();
if (companySearch.getEmail() != null && !companySearch.getEmail().equals("")){
builder.and(company.email.eq(companySearch.getEmail()));
}
if (companySearch.getCompanyName() != null && !companySearch.getCompanyName().equals("")){
builder.and(company.companyName.eq(companySearch.getCompanyName()));
}
//add other clauses...
return loginRepository.findAll(builder.getValue(),pageableService.getPageRequest(pageNumber));
}
..and this works fine. But it seems like an unnecessary amount of plumbing since I'll have to write similar, longwinded conditional code for each entity I'm working with. I reckon that reflection might be an option, but I'm not sure if QueryDSL has something built in that handles this situation. I've looked at the QueryDSL docs and nothing jumped out at me.
So is there a nice, tidy way of handling this situation without clogging up my service classes with boilerplate?
You can use Spring Data's QueryDSL integration. Basically, you extend the QueryDslPredicateExecutor in your repository interface and it add a findAll method that gets a QueryDSL Predicate and filter all the results based on that Predicate. You see more details here.
It turns out that the exact thing I was looking for is Spring Data's query by example API.
https://www.baeldung.com/spring-data-query-by-example
It lets you create a query by providing a sample entity and a matcher which defines things like case sensitivity, partial 'like' matching and so on.
It's very useful in limited situations, and can drastically reduce boilerplate query code; but when you want to query a more complex graph of data you'll want to use a different approach.
With Spring JPA is there an easy way to use native queries but maintaining database independence, for example by using the query which fits best?
At the moment I do this by checking the currently set Dialect from the Environment and call the proper method of my Repository:
public Foo fetchFoo() {
if (POSTGRES_DIALECT.equals(env.getRequiredProperty("hibernate.dialect"))) {
return repo.postgresOptimizedGetFoo();
}
return repo.getFoo();
}
This works but I have the feeling that there is a better way or that I am missing something. Especially because (Spring) JPA allows it to use native queries quite easily but that breaks one of its big advantages: database independence.
As per my understanding, this can be achieved simply by using #Transactional(readOnly=false) and then instead of calling session.createQuery, one can use session.createSQLQuery, as provided in this example.
Your sql can be any of your native query.
Hope this works for you. :)
#Override
#Transactional(readOnly = false)
public Long getSeqVal() {
Session session = entityManager.unwrap(Session.class);
String sql = "SELECT nextval('seqName')";
Query query = session.createSQLQuery(sql);
BigInteger big = (BigInteger) query.list().get(0);
return big.longValue();
}
This is just an idea: I do not know whether it works or not:
My idea would be having subinterfaces, one normal Spring-Data-JPA-interface with all methods for one entiy (without native query hints). Than I would crate a subinterface for every database, that "override" the database specific native statements. (This intrface would be empty if there are no DB specific statements). Then I would try configure Spring-JPA with some profiles to load the right specific interface (for example by a class-name or package-name-pattern)
This seems like a way to complicated way to get queries to work.
If you really want to use optimized queries make it at least transparant for your code. I suggest using named queries and create an orm.xml per database (much like Spring Boot uses to load the schema.xml for a different database).
In your code you can simply do
public interface YourRepository extends JpaRepository<YourEntity, Long> {
List<YourEntity> yourQueryMethod();
}
This will look for a named query with the name YourEntity.yourQueryMethod. Now in your orm.xml add the named query (the default one and in another one the optimized one).
Then you need to configure your LocalContainerEntityManagerFactory to load the specific one needed. Assuming you have a property defining which database you use, lets name it database.type you could do something like the following
<bean class="LocalContainerEntityManagerFactoryBean">
<property name="mappingResources" value="classpath:META-INF/orm-${database.type}.xml" />
... other config ...
</bean>
This way you can keep your code clean of the if/then/else construct and apply where needed. Cleans your code nicely imho.
i'm trying to convert a 'TableController'-Class we used (without ORM) to generate dynamic SQL (actually the order column and direction is appended to the SQL).
Think of this 'TableController' as a class that have a function to return a list of Entities of a given class (known at runtime), in a given order (String column/property name, boolean asc/desc, both at runtime).
The challenge is now, with JPA (Hibernate - but the customer requires to use JPA Interfaces only): How can i realize this without String concatenation, and in a type safe manner?
Thanks!
The challenge is now, with JPA (Hibernate - but the customer requires to use JPA Interfaces only): how can I realize this without String concatenation, and in a type safe manner?
If you're using a JPA 2.0 implementation, I think you should look at the Criteria API to build dynamic queries.
If you're using JPA 1.0, there is no standard way apart from String concatenation (and my suggestion would be to use Hibernate's proprietary Criteria Queries).
The following article might also give you some (concrete) ideas: Hibernate Querying 102 : Criteria API.
Imagine a method that has three parameters: Class entityClass, String orderByColumn, boolean ascending. How would i create a query without string concatenation that gives me all objects of the given class in the correct order?
With the Criteria API from JPA 2.0, you could do something like this:
public <T> List<T> findAllEntitiesOrderedBy(Class<T> entityClass, String orderByColumn, boolean ascending) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(entityClass);
Root<T> entityRoot = criteria.from(entityClass);
criteria.select(entityRoot);
javax.persistence.criteria.Order order = ascending ? builder.asc(entityRoot.get(orderByColumn))
: builder.desc(entityRoot.get(orderByColumn));
criteria.orderBy(order);
return em.createQuery(criteria).getResultList();
}