Query database with JPA for null values - spring

The value in database can be sometimes NULL and sometimes not. How can I retrieve it?
This is my try which makes me suprised:
#Repository
public interface AddressRepo extends JpaRepository<Address, Long>{
#Query("select count(a) > 0 from Address a where a.street = :street")
boolean testtest(#Param("street") String street);
}
test OK:
// given
address = new Address("WIELKA WARSZAAAWA", "Bokserska", "xxx", "50-500");
// when
addressRepo.save(address);
// then
assertTrue(addressRepo.testtest("Bokserska")); // OK
test fails:
// given
address = new Address("WIELKA WARSZAAAWA", null, "xxx", "50-500");
// when
addressRepo.save(address);
// then
assertTrue(addressRepo.testtest(null)); // cuz false!

The JPQL is not able to translate this statement:
WHERE a.street = null
To this SQL:
WHERE a.street IS null
So, you need to create a new #Query:
select count(a) > 0 from Address a where a.street IS NULL
Mount manually the JPQL string or use Criteria to create a dynamic query are also good options.

Related

How to check record is fully empty on left join in jooq query

I try to fetch a record from table with left join on another table. An information in the second table can be not found but I expect an information from the first table.
val citizenship = Tables.COUNTRIES.`as`("citizenship")
try {
return context.selectFrom(Tables.CLIENT_PROJECTIONS
.leftJoin(citizenship).on(
Tables.CLIENT_PROJECTIONS.CITIZENSHIP_COUNTRY_CODE.eq(
citizenship.CODE_ALPHA2
)
)
).where(Tables.CLIENT_PROJECTIONS.ID.eq(id)).fetchOne {
val clientProjection = ClientProjectionMapper.map(it.into(Tables.CLIENT_PROJECTIONS)) ?: return#fetchOne null
clientProjection.citizenship = CountryMapper.map(it.into(citizenship))
clientProjection
}
} catch (ex: DataAccessException) {
logger.error("Failed to access to database", ex)
throw ex
}
I convert data from CountriesRecord to Entity in CountryMapper:
object CountryMapper : RecordMapper<CountriesRecord, Country> {
override fun map(record: CountriesRecord?): Country? = when {
record != null -> {
Country(
countryCode = record.codeAlpha,
title = record.title
)
}
else -> {
null
}
}
}
But if query returns null in every fields of CountriesRecord my map method receive a non-nullable entity but everyone fields of this entity is empty.
I can check every field of CountriesRecord is it null but i think that isn't good idea. Can I check it by another more best way? May be I should write more correct query to database?
A LEFT JOIN in SQL does exactly that. It produces nulls for all columns of the left joined table for which there was no match in the join's ON clause.
You don't have to check whether each column is null. The primary key will be good enough, because that should have a NOT NULL constraint on it, meaning that if the primary key value is null (record.codeAlpha), then that must be because of the LEFT JOIN.
Change your second mapper to this:
object CountryMapper : RecordMapper<CountriesRecord, Country> {
override fun map(record: CountriesRecord?): Country? = when {
record?.codeAlpha != null -> {
Country(
countryCode = record.codeAlpha,
title = record.title
)
}
else -> {
null
}
}
}

Why can JPQLs modifying queries only return void or int?

When i want to modify the database via JPQL i have to mark the query as Transactional and Modiyfing. If i do so, the return type of the method representing the query has to be either void or int(representing the number of edited rows i think). Why are only the two return types allowed? If i do a HTTP-PUT request and update the object with an own JPQL query, i would like to return the updated object again. Whats the best way to do it if the return type of the query has to be void or int? Do i have to do a seperate query/request again which selects the object after it was updated?
EDIT:
Thats how i call the query:
if (inactivityListDTO.getProjectIds().size() > 0) {
projectRepository.updateProjectsIsArchivedByProjectIds(inactivityListDTO.getProjectIds(), inactivityListDTO.getIsArchived());
}
Thats the query:
#Transactional
#Modifying
#Query("UPDATE Project project SET project.isArchived = :isArchived,
project.archivedDate = current_date " +
"WHERE project.id IN :ids")
void updateProjectsIsArchivedByProjectIds(#Param("ids") List<Long> ids, #Param("isArchived") boolean isArchived);
Because it finally boils down to execute a standard UPDATE SQL in the DB , and the UPDATE in standard SQL only returns the number of records being updated and does not return a result set.
And yes , if you need get a record 's value after update , you have to query it again. Alternatively , you should consider using a JPA way to update a record , which first query the object , then update it by changing its state . Something like below (Assume you are using spring #Transactional to manage the transactional boundary):
#Transactional
public void changeEmployeeSalary(Integer employeeId , Integer salary){
Employee employee = entityManager.find(Employee.class , employeeId);
employee.setSalary(salary);
}
In this way , you do not need to query the record again after it is updated and you also do not need to manually write a UPDATE SQL.

Combine parameters and hardcoded clauses in Spring JPA #Query

I have a user repository in the application which works nicely for cases like this one:
#Query(" FROM UserEntity ue WHERE ue.site.id = :site_id
and ue.name = :username")
User findByUsername(#Param("site_id") String siteId,
#Param("username") String userName);
There is now a new option in one of the user fields, which should prevent the user to appear anywhere in the application. So instead of modifying the whole application, I've decided to modify just the queries in repositories with hardcoded clause like this:
#Query(" FROM UserEntity ue WHERE ue.site.id = :site_id
and ue.name = :username and ue.state != 'Disabled'")
User findByUsername(#Param("site_id") String siteId,
#Param("username") String userName);
(The changed part is and ue.state != 'Disabled')
The problem is, that such query doesn't return anything no matter what the value of state is.
I've also tried ue.state not like 'Disabled', but with the same result.
I've seen a lot of examples of using #Query, but didn't find any with hardcoded clauses. Is that even possible?
Yes you can pass the hardcoded values without changing the method signature.
JPA repository
#Query(" FROM Users ue WHERE ue.userLogin = :userLogin and ue.userName != 'admin'")
public Users cehckeme(#Param("userLogin") String userLogin);
Test Code
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring_model.xml");
usersRepository = (UsersRepository) ctx.getBean("usersRepository");
System.out.println(usersRepository.cehckeme("pthakre").getUserLogin());
Genrated query
Hibernate:
select
*
from
( select
users0_.USER_ID as USER1_2_,
users0_.USER_LOGIN as USER2_2_,
users0_.USER_NAME as USER3_2_,
users0_.USER_PASSWORD as USER4_2_
from
MED_USERS users0_
where
users0_.USER_LOGIN=?
and users0_.USER_NAME<>'admin' )
where
rownum <= ?

Spring JPA repoistory findBy IN List - allow null

Short Description
How do I make findBy<Field>In work with IN when the array list input is null. e.g. ignore it. What would your DAO for this look like?
Longer description.
Imagine you have creating a search for users page.
in the application. You have various options to filter on.
created (date range always given)
Country (when null ignore and search all countries)
AgeRange
Job Title
etc...
Now say you want to search for all users in a given date range in a list of countries.
When searching for users I will always search for a date joined however if I have not selected a country I want it to search for all countries.
I am planning on adding several more filter options other than country. So I don't really want to create lots of findBy methods for each possible field combination.
DAO
#Repository
public interface UserDao extends JpaRepository<User, Long> {
public List<BeatRate> findByCreatedBetweenAndCountryIn(Date from, Date to, ArrayList<String> countryList );
}
Test
#Test
public void test() throws ParseException {
Date from = new SimpleDateFormat( "yyyy-MM-dd" ).parse( "2015-01-01" );
Date to = new SimpleDateFormat("yyyy-MM-dd").parse("2015-05-15");
//ArrayList<String> countryList = new ArrayList<String>();
//countryList.add("UK");
//countryList.add("Australia");
//countryList.add("Japan"); // works ok when I have a list
countryList = null; // I want it to search for all countries when this is null -- this errors and doesnt work..
List<BeatRate> beatRates = beatRateDao.findByCreatedBetweenAndRentalCountryIn(from, to, countryList);
Assert.assertTrue(beatRates.size()>0);
}
You can have two methods:
beatRateDao.findByCreatedBetweenAndRentalCountryIn(from, to, countryList);
and
beatRateDao.findByCreatedBetweenAndRental(from, to);
Then simply pick one based on countryList:
List<BeatRate> beatRates = (countryList != null && !countryList.isEmpty())
? beatRateDao.findByCreatedBetweenAndRentalCountryIn(from, to, countryList)
: beatRateDao.findByCreatedBetweenAndRental(from, to);
The IN clause requires a non-nullable and non empty argument list as otherwise the query will fail.
On PostgreSQL, if you try to run a query like this:
select *
from product
where quantity in ( )
you get the following error:
ERROR: syntax error at or near ")"
LINE 3: where quantity in ( )
^
********** Error **********
ERROR: syntax error at or near ")"
SQL state: 42601
Character: 45

Using distinct in Spring data over multiple columns

My domain model is like this:
CollectedData {
String name;
String description;
int count;
int xAxis,
int yAxis
}
Using Spring data repository query, I would like to retrieve all the unique rows (unique with name, xAxis, yAxis)
I am trying something like this
#Query("select distinct a.name, a.xAxis, a.yAxis from CollectedData a")
List<CollectedData> findAllDistinctData();
So, when I do
List<CollectedData> records= findAllDistinctData();
for (CollectedData record : records) { //Exception on this line
}
Exception
[Ljava.lang.Object; cannot be cast to CollectedData.
Is there any other way to write query for this ?
#Query return ArrayList of Object(s) instead of specific type of object. so you have to define some thing like
#Query("select distinct a.name, a.xAxis, a.yAxis from CollectedData a")
List<Object> findAllDistinctData();
then cast according to your requirement,
List<Object> cdataList=findAllDistinctData();
for (Object cdata:cdataList) {
Object[] obj= (Object[]) cdata;
String name = (String)obj[0];
String description = (String)obj[1];;
...
}
Instead of returning an object you can use JPA's constructor expression feature to return a more specific object holding only the columns you're interested in. See also following answer:
JPQL Constructor Expression - org.hibernate.hql.ast.QuerySyntaxException:Table is not mapped
According to your example you could create a new Object with only the columns you are interested in:
SELECT DISTINCT new com.mypackage.MyInterestingCollectedData(a.name, a.xAxis, a.yAxis) from CollectedData a
If you want to select complete object based on distinct values of multiple columns,
In that case the native query would be the option.
e.g.
#Query(
value = "select distinct on (column1, column2, column3) * From my_table where someId=: order by column1 asc,column2 desc,column3 desc,column4 desc",
nativeQuery = true
)
fun finalAllDistinctBy(containerId: String): List<MyTable>

Resources