How to make method in repository like GreaterThanOrEquals in Spring Data Rest..? - spring-data-rest

How to make method on entity like GreaterThanOrEquals in Spring Data Rest..?
Entity class looks like below.
Class Demo {
private Long id;
private Long number;
}
Repository class looks like below.
interface DemoRepository extends JPARepository<Demo, Long>{
Collection<Demo> findByIdAndNumberGreaterThanZero(Long id, Long number);
}
The above code will works.? If not so how could i achieve this with out #Query.?

The method name is mistyped it should be findByIdAndNumberGreaterThanEqual, not findByIdAndNumberGreaterThanZero.
Collection<Demo> findByIdAndNumberGreaterThanEqual(Long id, Long number);
Also, if the id property is the actual id of the entity this method will return zero or one result. (Either the entity with the given id, if the number of that entity is greater or equal or nothing at all)
So it should be
Optional<Demo> findByIdAndNumberGreaterThanEqual(Long id, Long number);
...or if you need all of the entities where the number property is greater than a given value:
Collection<Demo> findByNumberGreaterThanEqual(Long number);

Related

Calculated date in WHERE condition of CDS view

I'm trying to get a list of valid system status for the notification object, in order to not check all the notifications in the table, I want to execute the selection by checking only the last 2 years of data.
Maybe there is a better solution to my problem, but I'm still curious about this technical limitation. To my knowledge, the system status in SAP are kind of hardcoded and can't be determined per object via any table (SAP could add new system status any moment).
I tried to create the below CDS view, but the function dats_add_months can't be used in the where condition, is there a solution to that? Notice that 7.50 doesn't have session parameter for system date so I use an environment variable:
define view ZNOTIF_SYS_STATUS
with parameters sydat : abap.dats #<Environment.systemField: #SYSTEM_DATE
as select distinct from qmel as notif
inner join jest as notif_status on notif_status.objnr = notif.objnr
and notif_status.inact = ''
inner join tj02t as sys_status on sys_status.istat = notif_status.stat
and sys_status.spras = $session.system_language
{
key sys_status.txt04 as statusID,
sys_status.txt30 as description
} where notif.erdat > dats_add_months($parameters.sydat, -12, '') //THIS CAN'T BE USED!!
Putting built-in functions in RHS position of WHERE is supported only since 7.51 and you have 7.50 as you said. That is why it works for Haojie and not for you.
What can be done here? Possible option is CDS table function which consumes AMDP-class. Consider this sample:
Table function
#EndUserText.label: 'table_func months'
define table function ZTF_MONTHS
with parameters
#Environment.systemField : #SYSTEM_DATE
p_datum : syst_datum
returns {
mandt : abap.clnt;
num : qmnum;
type : qmart;
}
implemented by method zcl_cds_qmel=>get_last_two_years;
AMDP
CLASS zcl_cds_qmel DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
TYPES: tt_statuses TYPE STANDARD TABLE OF qmel.
CLASS-METHODS get_last_two_years FOR TABLE FUNCTION ztf_months.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_cds_qmel IMPLEMENTATION.
METHOD get_last_two_years BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY.
twoyrs := add_months (CURRENT_DATE,-12)
RETURN SELECT mandt, qmnum AS num, qmart AS type FROM qmel WHERE erdat > twoyrs;
ENDMETHOD.
ENDCLASS.
It is very simplified compared to your original task but gives you the idea how to do this.

passing variable to parent class in Kotlin [duplicate]

Noob question here, I am working my way through a Udemy beginner Kotlin course and I can't work out why my age parameter isn't used when I use my derived class, but will work when my base class is used.
Person Class
open class Person(open var firstname: String, open var surname: String,
open var age: Int) {
val thisyear: Int = Calendar.getInstance().get(Calendar.YEAR)
val dob = thisyear - age
fun printperson() {
println("$firstname $surname was born in $dob")
}
}
Student Class
class Student(override var firstname: String, override var surname:
String, override var age: Int, val studentID: Int):
Person(firstname, surname, age) {
fun returnDetails() {
println("Hello $firstname, your Student ID is: $studentID")
}
}
Main
fun main(args: Array<String>) {
val studentMike = Student(firstname = "Mike",
surname = "Stand", age = 67, studentID = 8899)
studentMike.printperson()
studentMike.returnDetails()
val personBill = Person(firstname = "Bill", surname = "Hook", age = 34)
personBill.printperson()
}
Output
Mike Stand was born in 2018
Hello Mike, your Student ID is: 8899
Bill Hook was born in 1984
As you can see Bill was a direct use of the method in the Person Class, whereas Mike was an indirect call, the age parameter should have been inherited from the Person Class through the Student Class...
Looking at the official Kotlin docs, it looks like the issue is to do with the "Derived class initialisation order" but for the life of me I can't quite grok how to correct this.
Thanks for your help,
PS apologies for the inaccurate terminology. Always glad of pointers to do better.
the documentation you are referring to, says explicitly "When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks"
this is exactly what is happening in your example. In the base class Person you use the open member age in the constructor (the line where you calculate the dob).
This should be avoided because when that line calculating dob is executed, age did not yet receive a value from the derived class.
Ok, I did not answer at the "How to correct this?" but I hope it helps in clarifying what is going on
What Umberto Cozzi said is right, it is to do with the fact that you're referring to an open value in the constructor. If you step through the code you'll see that the sequence of events is:
You call the Student constructor, passing in a value for age.
The first thing that constructor does (before constructing the Student object) is call the superclass's constructor.
During the construction of the superclass (Person) you hit this line of code: val dob = thisyear - age. age is an open member (i.e. overridden in the subclass, Student). So the value should be retrieved from Student. The problem is that at this point the Student hasn't been fully constructed yet (remember the first thing its constructor did was call the superclass's constructor). So it's not possible yet for Person to ask Student what value age should be, as that value hasn't been set yet. If you step through the code and check out the value of age at this line of code, it's zero (the default value for an Int).
So the question is what to do? And I think the thing you should ask is: why does Student override age (and indeed firstname and surname). Is there any different in behaviour of age, firstname and surname between Person and Student? The answer is probably no. So Student shouldn't override these properties: instead it should declare them simply as constructor parameters (without val or var) and pass those values to the base class. In other words, Student should look as follows:
class Student(firstname: String, surname: String, age: Int, val studentID: Int) :
Person(firstname, surname, age) {
...
You might also want to be aware of the fact that your line of code that declares thisyear actually creates a property of Person, called thisyear, which I guess you don't want. Any val or var members that are declared directly in the class (rather than in a function) are a declaration of a property (and this is why this is all being calculated immediately during the construction of the Person object). So you might well want to inline this as:
val dob = Calendar.getInstance().get(Calendar.YEAR) - age
If the calculation is more complex and requires more lines of code, just create a private method (e.g. calculateDob) and call that, e.g. val dob = calculateDob(age)
There's also the slight anomaly that age is a var (i.e. can change) whereas dob is a val (i.e. can't change). So a user could change the value of age, but dob won't be updated. One possible way to solve this is to change dob so that instead of it being a property which is assigned a (read-only) value during construction, make it a property with a getter which will calculate the value every time it's called, e.g.
val dob
get() = Calendar.getInstance().get(Calendar.YEAR) - age
Another option is to add a backing field and getter/setter for age and update dob whenever age is updated.

Selecting specific columns using linq: What gets transferred?

I refer to this example: Return selected specified columns
Quote:
If BlobDetails isn't the LINQ entity, then you can do it directly:
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new BlobDetails {
Id = b.Id, Size = b.Size,
Signature = b.Signature, RowVersion = b.RowVersion};
return qry.ToList();
I see that they are selecting specific column in a query through the ORM-tool LINQ TO SQL.
Critics of ORM-tools say that, if I remember correctly, that ORM-tools select and return entire objects from the table, and limits the options of selecting only specific columns as one can do through classic SQL-programming. Of course, I have my doubts about that when I see this example, but nevertheless, I still keep asking myself the question: Does the database return only the selected columns, or does it return the entire objects, leaving the column-filtering to the ORM-tool?
From this example, they also have a class called Blobdetails:
public class BlobDetails
{
public int Id { get; set; }
public string Signature { get; set; }
public int Size { get; set; }
public System.Data.Linq.Binary RowVersion { get; set; }
}
Do I need to create my own classes everytime I only wish to select a few columns from a table through LINQ?
You don't need to create new classes to select few columns from a table. You can use anonymous types for that.
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new { b.Id, b.Size, b.Signature, b.RowVersion};
return qry.ToList();
Only selected columns are transferred. There is no difference between using plain SQL and using LINQ to SQL. When you are executing LINQ query, it is converted to plain SQL and executed. Then result is mapped to your objects.
You can use SQL Server Profiler to see what query was generated and executed on server. Also you can use LINQPad to see what SQL will be generated from your query. In your case query will be same either you use BlobDetails or anonymous object:
SELECT [t0].[Id], [t0].[Size], [t0].[Signature], [t0].[RowVersion]
FROM [Blobs] AS [t0]
ORDER BY [t0].[RowVersion] DESC
when you do projections LINQ does indeed only select those columns and there is nothing preventing you from materializing it however you want. So in your example code
select new BlobDetails
{
Id = b.Id,
Size = b.Size,
Signature = b.Signature,
RowVersion = b.RowVersion
};
Only b.id, b.size, b.signature, & b.rowversion are selected. You can verify this with sql profiler or your debugger, I seem to recall there is also a function you can call on the datacontext to get the last query that was ran.
I think that the answer to your first question is already in the POST you mentioned. However...
If your BlobDetails is not LINQ entity you can simply use it in your select statement to define (shrink) your projection attributes. For example:
var qry = from b in dc.Blobs
select new BlobDetails { Id = b.Id, Size = b.Size }
would compile to SQL query like SELECT Id, Size FROM Blob ....
But if BlobDetails is LINQ entity you will need to use that AsEnumerable() hack otherwise you will get NotSupportedException: Explicit construction of entity type in query is not allowed.
var qry = from b in dc.Blobs.AsEnumerable()
select new BlobDetails { Id = b.Id, Size = b.Size }
Edit
As #Chris Pitman stated in his comment this AsEnumerable() approach could create serious bottleneck, beacause the whole table would be loaded in memory before applying the projection. So it is not recommended!
To your second question:
You will need to create custom class for objects that you want use easily outside the scope of the method. Properties of an anonymous object are visible only in the scope, where they have been declared and anonymous objects can be cast only to type object.
So if you want to return anonymous objects from method the return type would has to be an enumerable of object or dynamic as #xeondev stated in his comment.
There's no need to create your own classes, you can return an anonymous type. You can write something like this
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new {
Id = b.Id, Size = b.Size,
Signature = b.Signature, RowVersion = b.RowVersion};
return qry.ToList();
Although the signature of the method should look to something like this
public IEnumerable<object> GetItems()
or
public dynamic GetItems()
So if you are going to use the result of linq query in outer scope like you example suggest, it is highly recommended you create your own classes.

Linq to sql - get value from db function and not directly from the db field (while mapping properties)

When you map a table to an object, every property created corresponds to one db column.
I want to execute a db function on a column before it gets mapped to the property, so the property gets the value returned by the db function, and not the column
I was trying to achieve that by Expression property of ColumnAttribute (as in the example below), so instead of BirthDate the usrFn_UTCToLocalTime(BirthDate) is returned
but it does not seem to be working and still gets pure value of the column.
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_BirthDate", DbType = "DateTime", UpdateCheck = UpdateCheck.Never, Expression = "dbo.usrFn_UTCToLocalTime(BirthDate)")]
public System.Nullable<System.DateTime> BirthDate
{
get
{
return this._BirthDate;
}
}
I have also modified the DBML XML as in:
other post on stackoverflow
but also without result.
Is that possible by using LINQ or do I have to overwrite a getter which costs roundtrip to the server?
According to the Remarks section on this MSDN page, the Expression property of the ColumnAttribute is used when calling CreateDatabase, so it won't work the way you intend unless you created your database with Linq to Sql.
You can create a Linq query that selects the various columns and calls the db function in one statement like this (based on MSDN example):
var qry = from person in db.Persons
select new {
FirstName = person.FirstName,
LastName = person.LastName,
BirthDate = person.BirthDate,
Dob = db.usrFn_UTCToLocalTime(person.BirthDate)
};
This projects into an anonymous type, but you could use a non-anonymous type as well. For the sake of the example, I've got a table named Person with FirstName, LastName, and BirthDate columns, and a user defined scalar function named usrFn_UTCToLocalTime. The sql statement sent to the server is:
SELECT [t0].[FirstName], [t0].[LastName], [t0].[BirthDate], CONVERT(DateTime,[dbo].[usrFn_UTCToLocalTime]([t0].[BirthDate])) AS [Dob]
FROM [dbo].[Person] AS [t0]
As I was suggesting in the question, for now I have overwritten the get method so I have:
get
{
using (var context = DB.Data.DataContextFactory.CreateContext())
{
return context.usrFn_UTCToLocalTime(_BirthDate);
}
//return this._BirthDate;
}
But with every access to the property, roundtrip is made - which is not my intention but gives a proper result.
I leave the question still open

return objects that are not in the mapping file from nhibernate hql

the title says pretty much what I'm trying to do. I have nhibernate hql with select case
select application.SubmissionDate, count(candidates)
from Application as application group by application.SubmissionDate
I would like to have the return values from this query into an object (which is not in nhibernate mapping file) called 'CountPerDay' object
class CountPerDay {
public DateTime Date,
public int Count
}
does nHibernate has sort of build in feature /methods to do this?
You should take a look at Ad-hoc mapping for NHibernate:
string hql = #"select application.SubmissionDate as Date, count(candidates) as Count
from Application as application
group by application.SubmissionDate";
var count = session.CreateQuery(hql)
.SetResultTransformer(Transformers.AliasToBean(typeof(CountPerDay)))
.List<CountPerDay>();