Simple Mybatis, how to obtain a collection? - mybatis

It has been a long time since I have done this, and I simply cannot see the pattern to code MyBatis to get a collection, after spending a lot of time searching the examples and Mybatis docs.
The problem I have is with the examples and answers lacking the code to interpret it properly, understand, and integrate into my own code... or it simply does not work, returning this error (in most cases):
org.apache.ibatis.exceptions.TooManyResultsException: Expected one
result (or null) to be returned by selectOne(), but found: 3
I have simplified the query and everything else I am trying to accomplish. What I need is a mapper in XML format as well as the Java interface, as a simple and a plain example that works ...
SQL statement I am trying to execute:
SELECT * FROM MyTable;
The structure of the MyTable
+----+-------+
| id | value |
+----+-------+
| 1 | a |
| 2 | ab |
| 3 | abc |
+----+-------+
The model is:
public class MyTable{
private short id = 0;
private String value;
public short getId() {
return id;
}
public void setId(short id) {
this.id = id;
}
public String getValue() {
return value
}
public void setValue(String value) {
this.value = value;
}
}
What I need to obtain is a
List<MyTable>
I am aware that there are similar questions, however, neither is providing the answer in xml format for the mapper, or the answers are not easy to understand lacking the code to support the answer ... Therefore, I want to keep this as simple as possible, for anyone else trying to connect things without a success.

With the limited information provided, all I can show you is a super simple example.
test/Mapper.xml
<mapper namespace="test.Mapper">
<select id="selectAll" resultType="MyTable">
select * from MyTable
</select>
</mapper>
test/Mapper.java
package test;
import java.util.List;
public interface Mapper {
List<MyTable> selectAll();
}
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Mapper mapper = sqlSession.getMapper(Mapper.class);
List<MyTable> list = mapper.selectAll();
}
Here is an executable demo project.

Related

How do I insert Postgres "infinity" into a Timestamp field with JOOQ?

I have a column defined like this:
expiry timestamp(0) without time zone not null
With Postgres, I can issue SQL like:
insert into my_table(expiry) values ('infinity')
I've been digging through the JOOQ doco, but couldn't find any example of dealing with this.
Can I do that with JOOQ - and what would it look like?
Additionally, is it possible using an UpdatableRecord? Is there some kind of infinity "flag" instance of Timestamp I can use?
Ok, found a way to do it directly.
MyRecord r = db.insertInto(
MY_RECORD,
MY_RECORD.ID,
MY_RECORD.CREATED,
MY_RECORD.EXPIRY
).values(
val(id),
currentTimestamp(),
val("infinity").cast(Timestamp.class)
).returning().fetchOne();
But that feels more like a workaround than the right way to do it. Casting a string to a timestamp seems a little bit round-about to me, so I wrote a CustomField to make using it and querying easier:
public class TimestampLiteral extends CustomField<Timestamp> {
public static final TimestampLiteral INFINITY =
new TimestampLiteral("'infinity'");
public static final TimestampLiteral NEGATIVE_INFINITY =
new TimestampLiteral("'-infinity'");
public static final TimestampLiteral TODAY =
new TimestampLiteral("'today'");
private String literalValue;
public TimestampLiteral(String literalValue){
super("timestamp_literal", SQLDataType.TIMESTAMP);
this.literalValue = literalValue;
}
#Override
public void accept(Context<?> context){
context.visit(delegate(context.configuration()));
}
private QueryPart delegate(Configuration configuration){
switch( configuration.dialect().family() ){
case POSTGRES:
return DSL.field(literalValue);
default:
throw new UnsupportedOperationException(
"Dialect not supported because I don't know how/if this works in other databases.");
}
}
}
Then the query is:
MyRecord r = db.insertInto(
MY_RECORD,
MY_RECORD.ID,
MY_RECORD.CREATED,
MY_RECORD.EXPIRY
).values(
val(id),
TimestampLiteral.TODAY,
TimestampLiteral.INFINITY
).returning().fetchOne();
Don't know if this is necessarily the "right" way to do this, but it seems to work for the moment.
Still interested to hear if there's a way to do this with an UpdatableRecord.
I create a java.sql.Timestamp passing org.postgresql.PGStatement.DATE_POSITIVE_INFINITY to its constructor.
create.insertInto(
MY_RECORD,
MY_RECORD.ID,
MY_RECORD.CREATED,
MY_RECORD.EXPIRY
).values(
1,
new Timestamp(System.currentTimeMillis()),
new Timestamp(PGStatement.DATE_POSITIVE_INFINITY)
).execute();

How to set values in ItemPreparedStatementSetter for one to many mapping

I am trying to use JdbcBatchItemWriter for a domain object RemittanceClaimVO . RemittanceClaimVO has a List of another domain object , ClaimVO .
public class RemittanceClaimVO {
private long remitId;
private List<ClaimVO> claims = new ArrayList<ClaimVO>();
//setter and getters
}
So for each remit id, there would be multiple claims and I wish to use single batch statement to insert all rows.
With plain jdbc, I used to write this object by putting values in batches like below ,
ist<ClaimVO> claims = remittanceClaimVO.getClaims();
if(claims != null && !claims.isEmpty()){
for(ClaimVO claim:claims){
int counter = 1 ;
stmt.setLong(counter++, remittanceClaimVO.getRemitId());
stmt.setLong(counter++, claim.getClaimId());
stmt.addBatch();
}
}
stmt.executeBatch();
I am not sure how to achieve same in Spring Batch by using ItemPreparedStatementSetter.
I have tried similar loop as above in setValues method but values not getting set.
#Override
public void setValues(RemittanceClaimVO remittanceClaimVO, PreparedStatement ps) throws SQLException {
List<ClaimVO> claims = remittanceClaimVO.getClaims();
for(ClaimVO claim:claims){
int counter = 1 ;
ps.setLong(counter++, remittanceClaimVO.getRemitId());
ps.setLong(counter++, claim.getClaimId());
}
}
This seems another related question.
Please suggest.

Can JRXML extend data source before using it?

I use the same data source for generating several reports. One of the reports needs to be printed with a few empty lines ("reserve") at the bottom, so that users can manually write missing data by hand if needed.
E.g.:
+---------+-------------+
| item 1 | bla bla |
+---------+-------------+
| item 2 | foo |
+---------+-------------+
| | | <--- here user can just add forgotten
+---------+-------------+ items with a pen
| | |
+---------+-------------+
The easiest for the JRXML would be if there were several records of nulls at the end of the data source. Then it would just print its "details" band a few times with no text, just as required. However, the data source is reused for other reports which certainly don't want this.
Can I somehow inject such empty lines into the data source in JRXML itself, just before report filling?
You can modify your CustomDataSource and set it from jrxml to generate extra records when needed:
Example
public class JRExtraEmptyRecordsDataSource extends JRBeanCollectionDataSource {
private int nrOfEmptyRecords=0;
private int currentExtraRecord = 0;
public JRExtraEmptyRecordsDataSource(Collection<?> beanCollection) {
super(beanCollection);
}
#Override
public Object getFieldValue(JRField field) throws JRException {
if (currentExtraRecord==0){
return super.getFieldValue(field);
}
Class<?> theCorrectClass = field.getValueClass();
//Implement your logic to return correct class (reflection or switch) if you need speciall values
//or just return null
return null;
}
#Override
public boolean next() {
boolean next = super.next();
if (next){
return true;
}
currentExtraRecord++;
return currentExtraRecord<=nrOfEmptyRecords;
}
#Override
public void moveFirst() {
super.moveFirst();
currentExtraRecord=0;
}
public int getNrOfEmptyRecords() {
return nrOfEmptyRecords;
}
public int setNrOfEmptyRecords(int nrOfEmptyRecords) {
this.nrOfEmptyRecords = nrOfEmptyRecords;
return this.nrOfEmptyRecords;//Lets return something for variable
}
}
When you need extra empty records add this variable definition in your jrxml
<variable name="extraRecords" class="java.lang.Integer">
<initialValueExpression><![CDATA[((my.package.JRExtraEmptyRecordsDataSource)$P{REPORT_DATA_SOURCE}).setNrOfEmptyRecords(2)]]></initialValueExpression>
</variable>
I do not have custom datasource what can I do?
Normally without custom datasource, you can use the columnFooter with the attribute isFloatColumnFooter="true" (or a dummy group footer band) to display extra info at the end of your detail band. Probably I would choose this method anyway since it helps to customize the extra rows and avoid checking for null in the normal detail band
If you need dynamically to indicate the number of empty records, include a subreport with an empty record and pass as datasource JREmptyDataSource
new net.sf.jasperreports.engine.JREmptyDataSource(P{nrOfEmptyRecords})

Play Framework 2.3 How to add unique constraint to sample application

Given the Play Framework 2.3 Computer Database sample application, I would like to practice adding a unique constraint on an attribute. Let's say I want the name attribute of the Computer class to be unique. I've tried to do this by adding a validate() function (and a getter) to Computer.java:
public List<ValidationError> validate() {
List<ValidationError> errors = new ArrayList<ValidationError>();
if(Computer.find.where().eq("name", getName()).findRowCount() != 0){
errors.add(new ValidationError("name", "Name must be unique. That value is already taken."));
}
return errors;
}
public String getName() {
return name;
}
This check works when creating new records in the database, however, this now causes a validation error when you update a Computer object but don't change the name. Is there a way to add a uniqueness constraint, similar to Rails? How can I validate uniqueness in Play?
Thanks!
UPDATE: see the answer by davide.
I ended up using the #Column(unique = true) constraint from the javax.persistence API. This doesn't generate an error in Play forms; instead, it throws a PersistenceException. Therefore I had to add change my controller to achieve the behavior I wanted. Both the create() and update() actions need a try/catch like this:
try {
computerForm.get().save();
} catch (PersistenceException pe) {
flash("error", "Please correct errors below.");
formData.reject("name", "Name conflict. Please choose a different name.");
return badRequest(createForm.render(computerForm));
}
UPDATE 2: each of the answers below is a possible solution
You need to exclude current entity from unique checking, i.e. like that:
if(Computer.find.where().eq("name", getName()).ne("id", getId()).findRowCount() != 0){
errors.add(new ValidationError("name", "Name must be unique."));
}
It will give you SQL query during update:
select count(*) from computer t0 where t0.name = 'Foo' and t0.id <> 123
And this during create:
select count(*) from computer t0 where t0.name = 'Foo' and t0.id is not null
P.S. ne() expression stands for Not Equal To and of course this approach assumes that your name field is Required
Edit: I sent you pull request with working solution, all you need is to add hidden field in your editForm like:
<input name="id" type="hidden" value='#computerForm("id").value'/>
Other thing is that you can simplify your model, i.e. don't need for getters for public fields.
I not sure if this answer your question, because I'm not familiar with Ruby syntax.
To "create a uniqueness constraint in the database" you can use the javax persistence API. Ebean will also recognize this.
To have a plain uniqueness constraint which involves a single field, you can use the #Column annotation:
#Entity
public class Computer extends Model {
...
#Column(unique = true)
public String name;
...
}
If you need some combination of fields to be unique, instead use the
#Table annotation
#Table(
uniqueConstraints=
#UniqueConstraint(columnNames={"name", "brand"})
)
#Entity
public class Computer extends Model {
...
public String name;
public String brand;
...
}
I hope it helps!

Playframework How to transform query.ResultList to VO

The version of Playframework is 1.2.x,and I want to transform the query.ResultList to VO.
I created a Part entity bean as below:
#Entity
#Table(name="evaluation_part")
public class Part extends Model {
public String name;
public String collegeName;
public int peopleNum;
}
The data:
id name collegeName peopleNum
1 Jsj1 JJJJ 32
2 Jsj2 JJJJ 23
3 Jsj3 JJJJ 32
4 Tjb1 TTTT 11
5 Tjb2 TTTT 14
6 Tjb3 TTTT 16
My value object class:
public class PartVO {
public String collegeName;
public int peopleNum;
}
And I want to use the native query to get the result:
String sql="select collegeName,SUM(peopleNum) as peopleNum from evaluation_part group by collegeName";
The query result is:
collegeName peopleNum
TTTT 41
JJJJ 87
I tried:
String sql="select collegeName,SUM(peopleNum) as peopleNum from evaluation_part group by collegeName";
Query query =JPA.em().createNativeQuery(sql);
List<PartVO> partVOs = query.getResultList();
for(int i=0;i<partVOs.size();i++) {
System.out.println(partVOs.get(i).collegeName);
}
Following error is what i am getting
ClassCastException occured : [Ljava.lang.Object; cannot be cast to valueobject.PartVO
You don't have to user raw sql to do that. With hql you can use the new operator to create your VO (see http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-select)
You have to define a two arg constructor in your partVO class, then you can do
select new package.PartVO(collegeName, SUM(peopleNum)) from Part group by collegeName
Solution 1:Only use 'select new Part()'(constructor defined in the Part class) in the HQL that u can convert object to Part.Hibernate use reflection to automatically inject all the fields u need.
Solution 2:Here the returned type of result must be Object[],so that u can got every field of the record fetched from database by the index of array;
The difference between solution1 and solution2:the previous use constructor in the query and the later transform a record into Object[].
In your case,ignore the complex relationship between entities,solutions above will make work.
Referenced Code here:
package controllers;
import play.*;
import play.db.jpa.JPA;
import play.mvc.*;
import java.util.*;
import models.*;
/**
* This demo is intended for fetching data from MYSQL.
* #author dhl#oopsplay.org
*/
public class Application extends Controller {
public static void index() {
render();
}
/**
* Prepare some data to test.
*/
public static void addPart() {
//Add a part record to database.
Part newPart=new Part("software","zjut",8).save();
if(newPart.isPersistent()){
renderText("Add successfully,there are %s records in the \'evaluation_part\' table.For convenience,please click the back button in the browser to go back previous page.",Part.count());
}
}
/**
* Fetch part entities from database;
*/
public static void fetchPart() {
//-------------------Solution 1-------------------
//[Pay attention]:Only use 'select new Part()'(constructor defined in the Part class) in the query that u can convert object to Part.
//Hibernate use reflection to automatically inject all the fields u need.
List<Part> parts1=JPA.em().createQuery("select new Part(name,collegeName,peopleNum) from Part").getResultList();
//For convenience, i output the detail in the console, focus on the change there.
Logger.info("The name of first record is :%s", parts1.get(0).name);
//-------------------Solution 2-------------------
//[Pay attention]:Here the returned type of result must be Object[],so that u can got every field of the record fetched from database;
List<Object[]> parts2=JPA.em().createNativeQuery("select name,collegeName,peopleNum from evaluation_part").getResultList();
Logger.info("The name of first record is :%s", parts2.get(0)[0]);
for(int i=0;i<parts2.size();i++){
//The difference between solution1 and solution2:the previous use constructor in the query and the later transform a record into Object[].
Logger.info("Name from parts1 is: %s", parts1.get(i).name);
Logger.info("Name from parts2 is: %s", parts2.get(i)[0]);
}
renderText("There are %s record in the \'evaluation_part\' table",parts2.size());
}
}
You can use the version of createNativeQuery(...) method that also accepts as argument the Class of the result instance(s):
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#createNativeQuery(java.lang.String, java.lang.Class).
However make sure this actually works, as Play Framework doesn't implement all the features of JPA in it's implementation of the API.