Spring Data R2DBC parameters conditional binding for SQL query - sql

I am facing a difficulty to bind a conditional parameters to SQL query using Spring Data R2DBC DatabaseClient. Two parameters can be null. Since the DatabaseClient requires to specify explicitly that the parameter is null, I have tried the following syntax but the conditional parameters were not appended to existing ones:
public Mono<Void> createAddress(Address address) {
DatabaseClient.GenericExecuteSpec bindings = databaseClient.execute(addressesQueries.getProperty("addresses.insert"))
.bind("line1", address.getLine1())
.bind("zipCode", address.getZipCode())
.bind("city", address.getCity())
.bind("countryId", address.getCountry())
.bind("id", address.getId()); // UUID
if(address.getLine2() == null) {
bindings.bindNull("line2", String.class);
} else {
bindings.bind("line2", address.getLine2());
}
if(address.getState() == null) {
bindings.bindNull("state", String.class);
} else {
bindings.bind("state", address.getState());
}
return bindings.fetch().rowsUpdated().then();
}
SQL query:
INSERT INTO addresses(id,line1,line2,zip_code,city,state,country) VALUES(:id,:line1,:line2,:zipCode,:city,:state,:countryId)
I know that I can split the SQL query to handle cases with/without null parameters but it will be a little bit complicated if I have more that one conditional parameter.
Do you know a solution that can help me to keep one SQL query and handle conditional parameters in Java code?

As commented, the bindings object is not changing with conditionals since you call bind and bindNull methods without saving such changed states back to object. Therefore line2 and state parameters are never populated with values. To fix, consider re-assigning bindings to update the object before its return:
if(address.getLine2() == null) {
bindings = bindings.bindNull("line2", String.class);
} else {
bindings = bindings.bind("line2", address.getLine2());
}
if(address.getState() == null) {
bindings = bindings.bindNull("state", String.class);
} else {
bindings = bindings.bind("state", address.getState());
}

Related

sort the table by column name Exposed Kotlin

Good afternoon, I want to make a universal sort for all tables. The idea is that the method will receive the name of the column as input and, through reflection, I will receive a link to the field of the same name.
val id = "id"
var a = JobSeekerTable::class
a.memberProperties.forEach { e ->
if (e.name == id) {
transaction {
JobSeeker.all().sortedBy { e.getter }
}
}
}
Unfortunately, this does not work. There was an option, through the fields field that the table has
JobSeekerTable.fields.forEach {v->
transaction {
JobSeeker.all().sortedBy { v }
}
}
but also unsuccessfully :(
If there is any way to refer to the required field through the name. Not using if and stuff like that?
First, you are probably looking for orderBy, not sortedBy. The former is to order SQL query results, the later is to sort a collection.
Second, you want to pass an instance of a column:
val id = "id"
JobSeekerTable.selectAll().orderBy(JobSeekerTable.columns.find {
it.name == id // Here I used the name you provided, although probably it should be named something like columnName
} !! to SortOrder.ASC)
Using "screaming" operator (!!) in Kotlin is a bad practice. So if all of your tables have ID column, for example, you can use "elvis" operator instead.
JobSeekerTable.selectAll().orderBy((JobSeekerTable.columns.find {
it.name == id
} ?: JobSeekerTable.id) to SortOrder.ASC)

Mono.zip with null

My code:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId())
)
.flatMap(...
From the frontend we get connect object with 2 fields:
connect{
aCredentialsId : UUID //required
bCredentialsId : UUID //optional
}
So sometimes the second line credentialService.getCredentials(connect.getBCredentialsId())) can return Mono.empty
How to write code to be prepared for this empty Mono when my second field bCredentialsId is null?
What should I do? In case of empty values return Mono.just(new Object) and then check if obj.getValue != null??? I need to fetch data from DB for 2 different values
The strategy I prefer here is to declare an optional() utility method like so:
public class Utils {
public static <T> Mono<Optional<T>> optional(Mono<T> in) {
return in.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty()));
}
}
...which then allows you to transform your second Mono to one that will always return an optional, and thus do something like:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId()).transform(Utils::optional)
).map(e -> new Connect(e.getT1(), e.getT2()))
(...assuming you have a Connect object that takes an Optional as the second parameter of course.)
An easier way is using mono's defaultIfEmpty method.
Mono<String> m1 = credentialService.getCredentials(connect.getACredentialsId());
Mono<String> m2 = credentialService.getCredentials(connect.getBCredentialsId()).defaultIfEmpty("");
Mono.zip(m1, m2).map(t -> connectService.connect(t.getT1(), t.getT2()));
Explanation: if m2 is null then get empty string as a default value instead of null.
Instead of using .zip here, I would work with a nullable property of Connect and use .flatMap in combination with .switchIfEmpty for it.
Kotlin-Version:
val aCredentials = credentialService.getCredentials(connect.getACredentialsId())
credentialService.getCredentials(connect.getBCredentialsId())
.flatMap { bCredentials -> aCredentials
.map { Connect(it, bCredentials)}
.switchIfEmpty(Connect(null, bCredentials))
}
.switchIfEmpty { aCredentials.map { Connect(it, null) } }

KEEPING a field "null" unless other information has been put in it through a form in SQL Server 2008

How do you preset fields so that (unless a specific value is entered from the form itself) they STAY null? For another project, I will later have to pull information from this table depending on what options people choose, so if I could do a cfif against nulls I think it'd be a lot easier than the blanks that are currently generated if I don't insert any new values.
Does anyone know where/how to do this? I'm using Microsoft's SQL Server Management Studio to edit what the individual columns, and all I can find are the command codes using INSERT, SELECT, etc., rather than having a list that I edit. Or is that the only way to make my default setting be "null"?
Thanks for the help
If a field is set to be a nullable field (that is, it allows NULL), when adding a row, it will be NULL unless otherwise specified.
You don't need to do anything special for this.
If your INSERT and UPDATE statements simply omit the field, it will not be updated, though you can specifically specify NULL for such a field if wanted.
You could use a helper function to handle parameter setup. This is a simplified version of a function I use...
private static object ProcessParameter(object input)
{
if (input == null)
return input;
switch (input.GetType().ToString().ToLower())
{
case "system.string":
if (input == null || input.ToString() == "") { return DBNull.Value; }
return input;
case "system.int32":
case "system.double":
if (input.ToString() == "0" && IsNullable(input)) { return DBNull.Value; }
return input;
case "system.datetime":
if (System.Convert.ToDateTime(input) == DateTime.MinValue || System.Convert.ToDateTime(input) == default(DateTime)) { return DBNull.Value; }
return input;
default:
return input;
}
}

How to access columns from an IQueryable when dynamically constructed?

I am using the System.Linq.Data library provided here - http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I have the following query which works great and returns an Iqueryable
IQueryable customer =
ctx.Customers.Where(cust => true).Select("new("Name,Address")");
However, how do I access these returned columns? I cannot access them using a lambda expression as follows:
var test = customer.Where(cust=>cust.Name == "Mike").First();
"cust.Name" in the above case cannot be resolved. It does not exist in the list of methods/properties for "cust".
Am i assuming something wrong here. I understand that I am working with an anonymous type. Do I have to create a DTO in this case?
For any IQueryable you have property called ElementType.
You can use it to get the properties as explained below
IQueryable query = from t in db.Cities
selec new
{
Id = t.Id,
CityName = t.Name
};
if(query!=null)
{
Type elementType = query.ElementType;
foreach(PropertyInfo pi in elementType.GetProperties())
{
}
}
Try foreach loop:
var a = _context.SENDERS.Select(x=>new { Address=x.ADDRESS, Company=x.COMPANY });
foreach(var obj in a)
{
Console.WriteLine(obj.Address);
Console.WriteLine(obj.Company);
}

NHibernate: Add criteria if param not null

I'm trying to retrieve a list of orders based on parameters specified by a user (basic search functionality). The user will enter either an orderId or a bunch of other params, those will get wrapped up into a message, and eventually make their way to the method below. My question is, how do I only look at the parameters that actually have values? So if a user were to enter a received date range and a store number and all other fields were null, I want to return orders for stores received in the date range and ignore all the null parameters. At first I was thinking I could use a conjunction, but I can't see a way to ignore the null parameters. Then I started splitting things out into the if statements below the main expression, but I don't want to look at those criteria if the user provides an externalId. Is there a simple way to do this?
public IList<Core.Order> GetOrderByCriteria
(
string ExternalId,
int? Store,
int? Status,
DateTime? beforeTransmissionDate, DateTime? afterTransmissionDate,
DateTime? beforeAllocationProcessDate, DateTime? afterAllocationProcessDate,
DateTime? beforeReceivedDate, DateTime? afterReceivedDate
)
{
try
{
NHibernate.ICriteria criteria = NHibernateSession.CreateCriteria(typeof(Core.Order))
.Add(Expression.Or
(
Expression.Like("ExternalId", ExternalId),
Expression.Conjunction()
.Add(Expression.Between("ReceivedDate", beforeReceivedDate, afterReceivedDate))
.Add(Expression.Between("TransmissionDate", beforeTransmissionDate, afterTransmissionDate))
.Add(Expression.Between("AllocationProcessDate", beforeAllocationProcessDate, afterAllocationProcessDate))
)
);
if(Store.HasValue)
criteria.Add(Expression.Eq("Status", Status));
if(Status.HasValue)
criteria.Add(Expression.Eq("Store", Store));
return criteria.List<Core.Order>();
}
catch (NHibernate.HibernateException he)
{
DataAccessException dae = new DataAccessException("NHibernate Exception", he);
throw dae;
}
}
I wound up dropping the whole conjunction thing and replacing the code in the try block with the code below. I also used joins which reduced the number of db accesses and reduced the amount of code needed.
NHibernate.ICriteria criteria = NHibernateSession.CreateCriteria(typeof(Core.Order));
if (!String.IsNullOrEmpty(ExternalId))
{
criteria.Add(Expression.Like("ExternalId", ExternalId));
}
if (beforeReceivedDate != null && afterReceivedDate != null)
criteria.Add(Expression.Between("ReceivedDate", beforeReceivedDate, afterReceivedDate));
if (beforeTransmissionDate != null && afterTransmissionDate != null)
criteria.Add(Expression.Between("TransmissionDate", beforeTransmissionDate, afterTransmissionDate));
if (beforeAllocationProcessDate != null && afterAllocationProcessDate != null)
criteria.Add(Expression.Between("AllocationProcessDate", beforeAllocationProcessDate, afterAllocationProcessDate));
if (Store.HasValue)
criteria.CreateCriteria("Store", "Store").Add(Expression.Eq("Store.LocationNumber", Store.Value));
return criteria.List<Core.Order>();
I had to do something similar not long ago. I'm pretty sure you can modify this to fit your needs.
private ICriteria AddSearchCriteria(ICriteria criteria, string fieldName, string value)
{
if (string.IsNullOrEmpty(fieldName))
return criteria;
if(string.IsNullOrEmpty(value))
return criteria;
criteria.Add(Expression.Like(fieldName, "%" + value + "%"));
return criteria;
}
The code calling the method ended up looking like this:
var query = session.CreateCriteria(typeof (User));
AddSearchCriteria(query, "FirstName", form["FirstName"]);
AddSearchCriteria(query, "LastName", form["LastName"]);
var resultList = new List<User>();
query.List(resultList);
return resultList;
Leave it up to the function to determine if the input is valid and whether to return the unmodified ICriteria or to add another Expression before returning it.