In the JOOQ documentation It says I can do this:
try (Connection c = getConnection()) {
String sql = "select schema_name, is_default " +
"from information_schema.schemata " +
"order by schema_name";
DSL.using(c)
.fetch(sql)
// We can use lambda expressions to map jOOQ Records
.map(rs -> new Schema(
rs.getValue("SCHEMA_NAME", String.class),
rs.getValue("IS_DEFAULT", boolean.class)
))
// ... and then profit from the new Collection methods
.forEach(System.out::println);
}
However when I do that I get the error "org.jooq.Schema is abstract; cannot be instantiated" - which if you look at documentation that's true.
So how in the world is the code in the example supposed to work?
Short answer : they are not using "org.jooq.Schema" in their example, but instead a static inner class.
If you scroll down at the bottom of the page you linked, they give github links to examples. The example you have is the SQL goodies one.
If you open SQLGoodies.java you'll notice a static inner class Schema at the top of the example class
static class Schema {
final String schemaName;
final boolean isDefault;
Schema(String schemaName, boolean isDefault) {
this.schemaName = schemaName;
this.isDefault = isDefault;
}
#Override
public String toString() {
return "Schema{" +
"schemaName='" + schemaName + '\'' +
", isDefault=" + isDefault +
'}';
}
}
Then scroll down and you'll find your example using the inner class :
DSL.using(c)
.fetch(sql)
.map(r -> new Schema(
r.getValue("SCHEMA_NAME", String.class),
r.getValue("IS_DEFAULT", boolean.class)
))
Related
I just wanted to know how to pass column name and its value to #Query annotation in Spring Data JPA.
Basically column names will be static and we used to put every column as a element in Entity class. But here I want something different, here column name will be dynamic I will be passing this value as Parameter to the method defined in repository.
Table - Calendar
Columns - id, PersonName, 1, 2, 3......31
Above is the table structure, 1,2,3,.....31 are the column names which represents calendar days and we have values in that columns. I'm using Spring Data JPA to fetch data from DB.
Here I just wanted to fetch person name for a particular day.
Below given the function defined in repository.
#Query("select c from Calendar c where :calendarDay=:value")
List<Calendar> getPersonName(#Param("calendarDay") String calendarDay, #Param("value") String value);
This is not working for me.
Any help would be appreciated.
The only dynamic parameter Spring JPA supports is #{#entityName}. Dynamic column names in #Query annotations are not supported., and that is what you are trying to accomplish.
Your only option is to construct a query manually using either QueryDSL, Specifications or Criteria API or simply by building a query string and passing it to your EntityManager. Regardless, you'll have to write code for that.
See, for instance:
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
How to add custom column name Spring Data JPA?
Take a look at sping data Specifications. You can find your solution there!
Reading the docs you can see that if Calendar is your domain (I would try to find a different name for my domain, there is a Calendar class in Java SE already), then you could use something like the above,
#Repository
public interface CalendarRepository extends JpaRepository<Calendar, Integer>, JpaSpecificationExecutor<Calendar> {
}
public class CalendarSpecification implements Specification<Calendar> {
private String randomColumnName; // A varchar column.
private String valueToSearchFor;
public CalendarSpecification(String randomColumnName, String valueToSearchFor) {
this.randomColumnName = randomColumnName;
this.valueToSearchFor = valueToSearchFor;
}
#Override
public Predicate toPredicate(Root<Calendar> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return builder.and(builder.equal(root.<String>get(this.randomColumnName), this.valueToSearchFor));
}
}
#Service
public class CalendarService {
#Autowired
private CalendarRepository calendarRepository;
public List<Calendar> findCustom(String randomColumnName, String valueToSearchFor) {
CalendarSpecification cs = new CalendarSpecification(randomColumnName, valueToSearchFor);
return calendarRepository.find(cs);
// Or using lambda expression - without the need of CalendarSpecification class.
// return calendarRepository.find((Root<ProductCategory> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> {
// return builder.and(builder.equal(root.<String>get(randomColumnName), valueToSearchFor));
// });
}
}
Maybe you can use CASE, WHEN.
SELECT
Id,
PersonName,
CASE
WHEN ? = 'day_01' THEN day_01
WHEN ? = 'day_02' THEN day_02
WHEN ? = 'day_03' THEN day_03
WHEN ? = 'day_04' THEN day_04
WHEN ? = 'day_05' THEN day_05'
ELSE 0
END
AS Value FROM Calendar
Java Code
// customize entity
public interface ITask {
Long getId();
String getName();
String getValue();
}
#Repository
public interface CalendarRepository {
static final String CASE_WHEN = "\nCASE\n"
+ " WHEN :field = 'day_01' THEN day_01\n"
+ " WHEN :field = 'day_02' THEN day_02\n"
+ " WHEN :field = 'day_03' THEN day_03\n"
+ " WHEN :field = 'day_04' THEN day_04\n"
+ " WHEN :field = 'day_05' THEN day_05\n"
+ " ELSE 0\n"
+ "END\n";
#Query(nativeQuery = true, value = "SELECT Id, PersoneName, " + CASE_WHEN + " AS Value FROM Calendar WHERE field = :field")
public List<ITask> findValues(#Param(value = "field") String field);
}
I am trying to send a transaction through the Java driver in my spring application.
The following is the simplified code.
#Test
public void rawTransactionTest(){
var appContext = new AnnotationConfigApplicationContext(DataLoaderApplication.class);
var arangoOperations = appContext.getBean(ArangoOperations.class);
String action = "function(){\n" +
" db = require(\"#arangodb\").db; \n" +
"db._query(\"LET doc = {title: \\\"Hello\\\"} "+
"UPSERT { _key: doc._key } INSERT doc._key == null ? UNSET(doc, \\\"_key\\\") : doc " +
"REPLACE doc IN Books OPTIONS { ignoreRevs: false } RETURN NEW\");\n" +
"return \"Success\"; \n" +
"}";
System.out.println(action);
var tOpts = new TransactionOptions();
tOpts.writeCollections("Books");
tOpts.waitForSync(true);
var result = arangoOperations.driver().db().transaction(action, String.class, tOpts);
System.out.println("Commit");
}
This returns the return value "Success" in the variable result. But the database remains unchanged. Doing the same thing in ArangoShell works perfectly fine. The ArangoShell code is as follows -
db._executeTransaction({
collections: {
write: ["Books"]
},
action: function(){
db = require("#arangodb").db;
db._query("LET doc = {title: \"Hello\"} UPSERT { _key: doc._key } "+
"INSERT doc._key == null ? UNSET(doc, \"_key\") : doc REPLACE doc"+
" IN Books OPTIONS { ignoreRevs: false } RETURN NEW");
return "Success";
}
});
This code works fine from the shell. Other non-transaction queries work fine from he same Spring-container.
What might be the problem?
The .db() only points to the _system database. Had to pass the database name to fix it.
I just wanted to know how to pass column name and its value to #Query annotation in Spring Data JPA.
Basically column names will be static and we used to put every column as a element in Entity class. But here I want something different, here column name will be dynamic I will be passing this value as Parameter to the method defined in repository.
Table - Calendar
Columns - id, PersonName, 1, 2, 3......31
Above is the table structure, 1,2,3,.....31 are the column names which represents calendar days and we have values in that columns. I'm using Spring Data JPA to fetch data from DB.
Here I just wanted to fetch person name for a particular day.
Below given the function defined in repository.
#Query("select c from Calendar c where :calendarDay=:value")
List<Calendar> getPersonName(#Param("calendarDay") String calendarDay, #Param("value") String value);
This is not working for me.
Any help would be appreciated.
The only dynamic parameter Spring JPA supports is #{#entityName}. Dynamic column names in #Query annotations are not supported., and that is what you are trying to accomplish.
Your only option is to construct a query manually using either QueryDSL, Specifications or Criteria API or simply by building a query string and passing it to your EntityManager. Regardless, you'll have to write code for that.
See, for instance:
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
How to add custom column name Spring Data JPA?
Take a look at sping data Specifications. You can find your solution there!
Reading the docs you can see that if Calendar is your domain (I would try to find a different name for my domain, there is a Calendar class in Java SE already), then you could use something like the above,
#Repository
public interface CalendarRepository extends JpaRepository<Calendar, Integer>, JpaSpecificationExecutor<Calendar> {
}
public class CalendarSpecification implements Specification<Calendar> {
private String randomColumnName; // A varchar column.
private String valueToSearchFor;
public CalendarSpecification(String randomColumnName, String valueToSearchFor) {
this.randomColumnName = randomColumnName;
this.valueToSearchFor = valueToSearchFor;
}
#Override
public Predicate toPredicate(Root<Calendar> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return builder.and(builder.equal(root.<String>get(this.randomColumnName), this.valueToSearchFor));
}
}
#Service
public class CalendarService {
#Autowired
private CalendarRepository calendarRepository;
public List<Calendar> findCustom(String randomColumnName, String valueToSearchFor) {
CalendarSpecification cs = new CalendarSpecification(randomColumnName, valueToSearchFor);
return calendarRepository.find(cs);
// Or using lambda expression - without the need of CalendarSpecification class.
// return calendarRepository.find((Root<ProductCategory> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> {
// return builder.and(builder.equal(root.<String>get(randomColumnName), valueToSearchFor));
// });
}
}
Maybe you can use CASE, WHEN.
SELECT
Id,
PersonName,
CASE
WHEN ? = 'day_01' THEN day_01
WHEN ? = 'day_02' THEN day_02
WHEN ? = 'day_03' THEN day_03
WHEN ? = 'day_04' THEN day_04
WHEN ? = 'day_05' THEN day_05'
ELSE 0
END
AS Value FROM Calendar
Java Code
// customize entity
public interface ITask {
Long getId();
String getName();
String getValue();
}
#Repository
public interface CalendarRepository {
static final String CASE_WHEN = "\nCASE\n"
+ " WHEN :field = 'day_01' THEN day_01\n"
+ " WHEN :field = 'day_02' THEN day_02\n"
+ " WHEN :field = 'day_03' THEN day_03\n"
+ " WHEN :field = 'day_04' THEN day_04\n"
+ " WHEN :field = 'day_05' THEN day_05\n"
+ " ELSE 0\n"
+ "END\n";
#Query(nativeQuery = true, value = "SELECT Id, PersoneName, " + CASE_WHEN + " AS Value FROM Calendar WHERE field = :field")
public List<ITask> findValues(#Param(value = "field") String field);
}
I need to query the db with the $where operator from Java Spring mongoTemplate.
This is the query:
db.myCollection.find( {$where : function () {
for (var index in this.*someKey*){
if (index.indexOf(*someValue*) > -1){
return this;
}
}
}})
But the mongoTemplate where operator expects to receive key not java script string function. Is there anyway around this?
I ended up using Spring MongoDB repositories(I needed paging as well)
public interface MyCollectionRepository extends PagingAndSortingRepository<MyCollectionClass, String> {
#Query("{$where : ?0}")
Page<MyCollectionClass> findSomething(String whereQuery, Pageable pageable);
.....
}
And
public static String whereQuery(String someValue){
return "function() {" +
"for (var index in this.*someKey*){" +
"if (index.indexOf(\""+ someValue+"\") > -1){" +
"return this;" +
"}" +
"}" +
"}";
}
I'm using Java to download HTML contents of websites whose URLs are stored in a database. I'd like to put their HTML into database, too.
I'm using Jsoup for this purpose:
public String downloadHTML(String byLink) {
String htmlInPage = "";
try {
Document doc = Jsoup.connect(byLink).get();
htmlInPage = doc.html();
} catch (org.jsoup.UnsupportedMimeTypeException e) {
// process this and some other exceptions
}
return htmlInPage;
}
I'd like to download websites concurrently and use this function:
public void downloadURL(int websiteId, String url,
String categoryName, ExecutorService executorService) {
executorService.submit((Runnable) () -> {
String htmlInPage = downloadHTML(url);
System.out.println("Category: " + categoryName + " " + websiteId + " " + url);
String insertQuery =
"INSERT INTO html_data (website_id, html_contents) VALUES (?,?)";
dbUtils.query(insertQuery, websiteId, htmlInPage);
});
}
dbUtils is my class based on Apache Commons DbUtils. Details are here: http://pastebin.com/iAKXchbQ
And I'm using everything mentioned above in a such way: (List<Object[]> details are explained on pastebin, too)
public static void main(String[] args) {
DbUtils dbUtils = new DbUtils("host", "db", "driver", "user", "pass");
List<String> categoriesList =
Arrays.asList("weapons", "planes", "cooking", "manga");
String sql = "SELECT lw.id, lw.website_url, category_name " +
"FROM list_of_websites AS lw JOIN list_of_categories AS lc " +
"ON lw.category_id = lc.id " +
"where category_name = ? ";
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (String category : categoriesList) {
List<Object[]> sitesInCategory = dbUtils.select(sql, category );
for (Object[] entry : sitesInCategory) {
int websiteId = (int) entry[0];
String url = (String) entry[1];
String categoryName = (String) entry[2];
downloadURL(websiteId, url, categoryName, executorService);
}
}
executorService.shutdown();
}
I'm not sure if this solution is correct but it works. Now I want to modify code to save HTML not from all websites in my database, but only their fixed ammount in each category.
For example, download and save HTML of 50 websites from the "weapons" category, 50 from "planes", etc. I don't think it's necessary to use sql for this purpose: if we select 50 sites per category, it doesn't mean we save them all, because of possibly incorrect syntax and connection problems.
I've tryed to create separate class implementing Runnable with fields: counter and maxWebsitesPerCategory, but these variables aren't updated. Another idea was to create field Map<String,Integer> sitesInCategory instead of counter, put each category as a key there and increment its value until it reaches maxWebsitesPerCategory, but it didn't work, too. Please, help me!
P.S: I'll also be grateful for any recommendations connected with my realization of concurrent downloading (I haven't worked with concurrency in Java before and this is my first attempt)
How about this?
for (String category : categoriesList) {
dbUtils.select(sql, category).stream()
.limit(50)
.forEach(entry -> {
int websiteId = (int) entry[0];
String url = (String) entry[1];
String categoryName = (String) entry[2];
downloadURL(websiteId, url, categoryName, executorService);
});
}
sitesInCategory has been replaced with a stream of at most 50 elements, then your code is run on each entry.
EDIT
In regard to comments. I've gone ahead and restructured a bit, you can modify/implement the content of the methods I've suggested.
public void werk(Queue<Object[]> q, ExecutorService executorService) {
executorService.submit(() -> {
try {
Object[] o = q.remove();
try {
String html = downloadHTML(o); // this takes one of your object arrays and returns the text of an html page
insertIntoDB(html); // this is the code in the latter half of your downloadURL method
}catch (/*narrow exception type indicating download failure*/Exception e) {
werk(q, executorService);
}
}catch (NoSuchElementException e) {}
});
}
^^^ This method does most of the work.
for (String category : categoriesList) {
Queue<Object[]> q = new ConcurrentLinkedQueue<>(dbUtils.select(sql, category));
IntStream.range(0, 50).forEach(i -> werk(q, executorService));
}
^^^ this is the for loop in your main
Now each category tries to download 50 pages, upon failure of downloading a page it moves on and tries to download another page. In this way, you will either download 50 pages or have attempted to download all pages in the category.