diff --git a/base/src/org/compiere/model/Query.java b/base/src/org/compiere/model/Query.java
index f8fcf7efb9..5e919a7d5a 100644
--- a/base/src/org/compiere/model/Query.java
+++ b/base/src/org/compiere/model/Query.java
@@ -39,10 +39,11 @@ import org.compiere.util.Util;
* @author Low Heng Sin
* @author Teo Sarca, SC ARHIPAC SERVICE SRL
*
FR [ 1981760 ] Improve Query class
- * BF [ 2030280 ] org.compiere.model.Query apply access fielter issue
+ * BF [ 2030280 ] org.compiere.model.Query apply access filter issue
* FR [ 2041894 ] Add Query.match() method
- * FR [ 2107068 ] Query.setOrderBy should be more error tollerant
+ * FR [ 2107068 ] Query.setOrderBy should be more error tolerant
* FR [ 2107109 ] Add method Query.setOnlyActiveRecords
+ * FR [ 2421313 ] Introduce Query.firstOnly convenient method
*/
public class Query
{
@@ -227,6 +228,58 @@ public class Query
return po;
}
+ /**
+ * Return first PO that match query criteria.
+ * If there are more records that match criteria an exception will be throwed
+ * @return first PO
+ * @throws DBException
+ * @see {@link #first()}
+ */
+ @SuppressWarnings("unchecked")
+ public T firstOnly() throws DBException
+ {
+ T po = null;
+ String sql = buildSQL(null, true);
+
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try
+ {
+ pstmt = DB.prepareStatement (sql, trxName);
+ rs = createResultSet(pstmt);
+ if (rs.next())
+ {
+ po = (T)table.getPO(rs, trxName);
+ }
+ if (rs.next())
+ {
+ throw new DBException("QueryMoreThanOneRecordsFound"); // TODO : translate
+ }
+ }
+ catch (SQLException e)
+ {
+ log.log(Level.SEVERE, sql, e);
+ throw new DBException(e);
+ }
+ finally
+ {
+ DB.close(rs, pstmt);
+ rs = null; pstmt = null;
+ }
+ return po;
+ }
+
+
+ /**
+ * red1 - returns full SQL string - for caller needs
+ * @return buildSQL(null,true)
+ *
+ */
+ public String getSQL() throws DBException
+ {
+ return buildSQL(null, true);
+ }
+
/**
* Count items that match query criteria
* @return count
diff --git a/extend/src/test/functional/QueryTest.java b/extend/src/test/functional/QueryTest.java
index 678b537666..61157957ad 100644
--- a/extend/src/test/functional/QueryTest.java
+++ b/extend/src/test/functional/QueryTest.java
@@ -11,7 +11,6 @@ import org.compiere.model.MTable;
import org.compiere.model.POResultSet;
import org.compiere.model.Query;
import org.compiere.util.DB;
-import org.compiere.util.Env;
import test.AdempiereTestCase;
@@ -149,4 +148,26 @@ public class QueryTest extends AdempiereTestCase
.first();
assertEquals("Invalid object", "C_Invoice", t.getTableName());
}
+
+ public void testFirstOnly() throws Exception
+ {
+ MTable t = new Query(getCtx(), "AD_Table", "AD_Table_ID=?", getTrxName())
+ .setParameters(new Object[]{318})
+ .firstOnly();
+ assertEquals("Invalid table ID", 318, t.get_ID());
+ //
+ Exception ex = null;
+ try
+ {
+ t = new Query(getCtx(), "AD_Table", "TableName IN (?,?)", getTrxName())
+ .setParameters(new Object[]{"C_Invoice", "M_InOut"})
+ .setOrderBy("TableName")
+ .firstOnly();
+ }
+ catch (DBException e)
+ {
+ ex = e;
+ }
+ assertNotNull("Exception should be throwed", ex);
+ }
}