JPA named query match a list of tuples in IN clause - sql

spring data jpa 1.4.3 with Oracle 11g.
I have an entity like this:
class LinkRecord {
String value;
int linkType;
...
}
I am using (value, linkType) as a composite index.
For a given list of (v, t) tuples, we need to select all the records in the DB so that value = v, linkType = t.
Basically, I want to build this query:
SELECT * FROM LINK_RECORD WHERE (VALUE, LINK_TYPE) IN (('value1', 0), ('value2', 25), ...)
where the list in the IN clause is passed in as a param.
Since we're working with a large volume of data, it would be very undesirable to query for the tuples one by one.
In my repository I've tried this:
#Query("select r from LinkRecord r where (r.value, r.linkType) in :keys")
List<LinkRecord> findByValueAndType(#Param("keys")List<List<Object>> keys);
where keys is a list of (lists of length 2). This gets me ORA_00920: invalid relational operator.
Is there any way to make this work using a named query? Or do I have to resort to native sql?

The answer is too late, but maybe some1 else has the same problem. This is one of my working examples. Here I need to search for all entries that match a given composite key:
The entity....
#Entity
#NamedQueries({
#NamedQuery(name = "Article.findByIdAndAccessId", query = "SELECT a FROM Article a WHERE a.articlePk IN (:articlePks) ORDER BY a.articlePk.article")
})
#Table(name = "ARTICLE")
public class Article implements Serializable
{
private static final long serialVersionUID = 1L;
#EmbeddedId
private ArticlePk articlePk = new ArticlePk();
#Column(name = "art_amount")
private Float amount;
#Column(name = "art_unit")
private String unit;
public Article()
{
}
//more code
}
The PK class....
#Embeddable
public class ArticlePk implements Serializable
{
private static final long serialVersionUID = 1L;
#Column(name = "art_article")
private String article;
#Column(name = "art_acc_identifier")
private Long identifier;
public ArticlePk()
{
}
public ArticlePk(String article, Long identifier)
{
this.article = article;
this.identifier = identifier;
}
#Override
public boolean equals(Object other)
{
if (this == other)
{
return true;
}
if (!(other instanceof ArticlePk))
{
return false;
}
ArticlePk castOther = (ArticlePk)other;
return this.article.equals(castOther.article) && this.identifier.equals(castOther.identifier);
}
#Override
public int hashCode()
{
final int prime = 31;
int hash = 17;
hash = hash * prime + this.article.hashCode();
hash = hash * prime + this.identifier.hashCode();
return hash;
}
//more code
}
Invocation by....
TypedQuery<Article> queryArticle = entityManager.createNamedQuery("Article.findByIdAndAccessId", Article.class);
queryArticle.setParameter("articlePks", articlePks);
List<Article> articles = queryArticle.getResultList();
where....
articlePks is List<ArticlePk>.

Related

Creating a class with implicit operators but it only works one way

A UDouble is an object that has units associated with the value (Value). The Unit is much more complex than shown here, but was converted to a string for the example.
public class UDouble
{
public string Unit { get; set; }
public double Value;
public UDouble(string unit)
{
Unit = unit;
}
public UDouble(string unit, double value)
{
Unit = unit;
Value = value;
}
public static implicit operator double(UDouble m)
{
return m.Value;
}
//public static implicit operator UDouble(double d)
//{
// return new UDouble("?????????", d); // wrong - units are lost
//}
}
static void Main()
{
UDouble p = new UDouble("mm", 2.3);
p.Value = 3;
double t = p;
p = 10.5;
}
The last line in the main() function (p = 10.5;) does not compile. What I what is the UDouble to act as if it is a double. In other words, I am trying to get p = 10.5; to work like p.Value = 10.5;
Is there a way to make it work?

optaplanner can't get the best solution, and different input orders produce different solutions

I'm trying to make a demo using optaplanner: there are some schemes, each scheme has attribute of gain and cost, and a scheme may conflict with one or more other schemes. The question is to find out a group of schemes which match following constraints:
hard constraint: selected schemea may not conflict with each other in this group
soft constraint: make the difference between total gain and total cost as high as possible
I built following code and try to resolve the question:
#PlanningEntity
#Data
#NoArgsConstructor
public class Scheme {
#PlanningId
private String id;
private int gain;
private int cost;
#PlanningVariable(valueRangeProviderRefs = {"validRange"})
// when valid is ture means this scheme will be selected into the solution group
private Boolean valid;
private Set<String> conflicts = new HashSet<>();
public void addConflict(String id) {
conflicts.add(id);
}
public Scheme(String id, int gain, int cost, String[] conflicts) {
this.id = id;
this.gain = gain;
this.cost = cost;
for (String s : conflicts) {
addConflict(s);
}
}
}
#PlanningSolution
public class SchemeSolution {
private HardSoftScore score;
private List<Scheme> schemeList;
#ProblemFactCollectionProperty
#ValueRangeProvider(id = "validRange")
public List<Boolean> getValidRange() {
return Arrays.asList(Boolean.FALSE, Boolean.TRUE);
}
#PlanningScore
public HardSoftScore getScore() {
return score;
}
public void setScore(HardSoftScore score) {
this.score = score;
}
#PlanningEntityCollectionProperty
public List<Scheme> getSchemeList() {
return schemeList;
}
public void setSchemeList(List<Scheme> schemeList) {
this.schemeList = schemeList;
}
}
And the constraint rule as below:
rule "conflictCheck"
when
Boolean(this==true) from accumulate (
$schs: List() from collect (Scheme(valid==true)),
init(boolean cfl = false;Set cfSet = new HashSet();List ids = new ArrayList()),
action(
for(int i = 0; i < $schs.size(); ++i) {
Scheme sch = (Scheme)$schs.get(i);
cfSet.addAll(sch.getConflicts());
ids.add(sch.getId());
}
for( int i = 0; i < ids.size(); ++i) {
String id = (String)ids.get(i);
if(cfSet.contains(id)) {
cfl = true;
return true;
}
}
),
result(cfl)
)
then
scoreHolder.addHardConstraintMatch(kcontext, -10000);
end
rule "bestGain"
when
$gc : Number() from
accumulate(
Scheme(valid==true, $gain : gain, $cost: cost),
sum($gain - $cost)
)
then
scoreHolder.addSoftConstraintMatch(kcontext, $gc.intValue());
end
Then I constructed three schemes as input of the test. Oddly, I found that optaplanner can't get the best solution, and different input orders produce different solutions.
When I set input as following:
private static List<Scheme> getSchemes() {
List<Scheme> ret = new ArrayList();
ret.add(new Scheme("S1", 5, 2, new String[]{"S3"}));
ret.add(new Scheme("S2", 3, 1, new String[]{"S3"}));
ret.add(new Scheme("S3", 10, 4, new String[]{"S1", "S2"}));
return ret;
}
the output is :
0hard/5soft
Scheme(id=S1, gain=5, cost=2, valid=true, conflicts=[S3])
Scheme(id=S2, gain=3, cost=1, valid=true, conflicts=[S3])
Scheme(id=S3, gain=10, cost=4, valid=false, conflicts=[S1, S2])
And when I set input as following:
private static List<Scheme> getSchemes() {
List<Scheme> ret = new ArrayList();
ret.add(new Scheme("S3", 10, 4, new String[]{"S1", "S2"}));
ret.add(new Scheme("S1", 5, 2, new String[]{"S3"}));
ret.add(new Scheme("S2", 3, 1, new String[]{"S3"}));
return ret;
}
I get the best solution and the output is :
0hard/6soft
Scheme(id=S3, gain=10, cost=4, valid=true, conflicts=[S1, S2])
Scheme(id=S1, gain=5, cost=2, valid=false, conflicts=[S3])
Scheme(id=S2, gain=3, cost=1, valid=false, conflicts=[S3])
Could anyone help me about it?

How to call an api and select correct optional from update method in spring mvc

I have been following online tutorials for MVC but have hit a snag with updating.
Have used a updateStudent() method in the controller and pass in the id, but inside the updateStudent() I have alot of optionals.
Trying to figure out how to call the api and select the optional from within the method I want it to use
Any help is appreciated
Thanks.
Controller....
public class StudentController {
public final StudentService studentService;
#Autowired
public StudentController(StudentService studentService) {
this.studentService=studentService;
}
#PutMapping(path="/updateme/{id}")
public void updateStudent(#PathVariable ("id") UUID id,#RequestBody Student student) {
studentService.updateStudent(id, student);
}
StudentService...
#Service
public class StudentService {
private final StudentDao studentDao;
//constructor
public StudentService(#Qualifier("postgres3")StudentDao studentDao) {
this.studentDao=studentDao;
}
#PutMapping
public void updateStudent(UUID id, Student student) {
Optional.ofNullable(student.getChapterProgress())
.filter(cp -> !StringUtils.isEmpty(cp))
.ifPresent(cp -> studentDao.updateChapterProgress(id, cp));
Optional.ofNullable(student.getAvgTestScore())
.filter(avg -> !StringUtils.isEmpty(avg))
.ifPresent(avg -> studentDao.updateAvgTestScore(id, avg));
Optional.ofNullable(student.getChap1Score())
.filter(c1 -> !StringUtils.isEmpty(c1))
.ifPresent(c1 -> studentDao.updateChap1Score(id, c1));
Optional.ofNullable(student.getChap2Score())
.filter(c2 -> !StringUtils.isEmpty(c2))
.ifPresent(c2 -> studentDao.updateChap2Score(id, c2));
Optional.ofNullable(student.getChap3Score())
.filter(c3 -> !StringUtils.isEmpty(c3))
.ifPresent(c3 -> studentDao.updateChap3Score(id, c3));
Optional.ofNullable(student.getChap4Score())
.filter(c4 -> !StringUtils.isEmpty(c4))
.ifPresent(c4 -> studentDao.updateChap4Score(id, c4));
Optional.ofNullable(student.getChap5Score())
.filter(c5 -> !StringUtils.isEmpty(c5))
.ifPresent(c5 -> studentDao.updateChap5Score(id, c5));
Optional.ofNullable(student.getChap6Score())
.filter(c6 -> !StringUtils.isEmpty(c6))
.ifPresent(c6 -> studentDao.updateChap6Score(id, c6));
}
StudentDataAccessService...
#Repository("postgres3")
public class StudentDataAccessService implements StudentDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public StudentDataAccessService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate= jdbcTemplate;
}
#Override
public int updateChapterProgress(UUID id, Integer chapterprogress) {
String sql = "UPDATE student SET chapterprogress = ? WHERE id = ?";
return jdbcTemplate.update(sql, chapterprogress, id);
}
#Override
public int updateAvgTestScore(UUID id, Double avg) {
String sql = "UPDATE student SET avgtestscore = ? WHERE id = ?";
return jdbcTemplate.update(sql, avg, id);
}
#Override
public int updateChap1Score(UUID id, Double chap1Score) {
String sql = "UPDATE student SET chap1score = ? WHERE id = ?";
return jdbcTemplate.update(sql, chap1Score, id);
}
#Override
public int updateChap2Score(UUID id, Double chap2Score) {
String sql = "UPDATE student SET chap2score = ? WHERE id = ?";
return jdbcTemplate.update(sql, chap2Score, id);
}
#Override
public int updateChap3Score(UUID id, Double chap3Score) {
String sql = "UPDATE student SET chap3score = ? WHERE id = ?";
return jdbcTemplate.update(sql, chap3Score, id);
}
#Override
public int updateChap4Score(UUID id, Double chap4Score) {
String sql = "UPDATE student SET chap4score = ? WHERE id = ?";
return jdbcTemplate.update(sql, chap4Score, id);
}
#Override
public int updateChap5Score(UUID id, Double chap5Score) {
String sql = "UPDATE student SET chap5score = ? WHERE id = ?";
return jdbcTemplate.update(sql, chap5Score, id);
}
#Override
public int updateChap6Score(UUID id, Double chap6Score) {
String sql = "UPDATE student SET chap6score = ? WHERE id = ?";
return jdbcTemplate.update(sql, chap6Score, id);
}
#Override
public int updateStudentById(UUID id, Student student) {
return 0;
}
StudentDao...
public interface StudentDao {
int updateStudentById(UUID id,Student student);
int updateChapterProgress(UUID id, Integer chapterprogress);
int updateAvgTestScore(UUID id, Double avg);
int updateChap1Score(UUID id, Double chap1Score);
int updateChap2Score(UUID id, Double chap2Score);
int updateChap3Score(UUID id, Double chap3Score);
int updateChap4Score(UUID id, Double chap4Score);
int updateChap5Score(UUID id, Double chap5Score);
int updateChap6Score(UUID id, Double chap6Score);
Ended up assigning each update to its own method call in the controller, thanks

Duplicate data from JPA query (sql constraint)

For some reason I'm returning 9 rows of duplicate data using this query in my repository.
#Query("select distinct profile from OfficeProfile profile where profile.fcoDesignCd in
(select officeLocation.asccode from OfficeLocation officeLocation, OfficeProfile profile
where officeLocation.statecode = :stateCode and officeLocation.asccode = profile.fcoDesignCd)")
public List<OfficeProfile> searchStateASC(#Param("stateCode") String stateCode);
The sql query that returns 9 distinct rows of data is below. The queries appear to be identical.
select
op.FCO_DESIGN_CD,
op.office_addr_line1,
op.office_addr_line2,
op.office_addr_state,
op.office_addr_zip
from cridba.office_profile op
where op.fco_design_cd in (
select asc_code from cridba.cris_lk_location cll , cridba.office_profile op
where cll.state_code='VA'
and cll.asc_code = op.fco_design_cd);
This is how I'm iterating over the values. I set my debugger and noticed the same 9 values with ids.
for(OfficeProfile locationInfo: officeLocatorRepository.searchStateASC(stateCode))
Here are my entity relationships.
Office Profile (Parent)
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "profile")
private Set<OfficeLocation> officeLocation = new HashSet<>(0);
Office Location (Child)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "asc_code", referencedColumnName = "fco_design_cd", nullable = false, insertable=false,
updatable=false)
public OfficeProfile profile;
I'm overriding equals and hashcode in both classes. Since I'm joining these tables using asc_code do i override that or id? or both? Here is what I have so far.
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OfficeProfile officeProfile = (OfficeProfile) o;
if (officeProfile.getId() == null || getId() == null) {
return false;
}
return Objects.equals(getId(), officeProfile.getId());
}
#Override
public int hashCode() {
return Objects.hashCode(getId());
}
Should I add #Id to fcoDesignCd even though this table already has an id? fcoDesignCd is the referenced column in join?
#Column(name = "fco_design_cd")
private String fcoDesignCd;
HQL output...
select distinct officeprof0_.office_type_id as office_type_id1_1_, ......
from cridba.office_profile officeprof0_ where officeprof0_.fco_design_cd in
(select officeloca1_.asc_code
from cridba.cris_lk_location officeloca1_, cridba.office_profile
officeprof2_ where officeloca1_.state_code=? and
officeloca1_.asc_code=officeprof2_.fco_design_cd)
Does this look like the right path to take? JPA How add unique contraint on column for #OneToMany relation like on username
You shouldn't add another #Id column for your table, since you already have one. Make sure that its backed up with a unique constraint in the database.
The overriding of hashCode and equals looks ok also.
The problem with duplicates is probably in the query.

OptaPlaner simple example cant find feasible solution

to get familiar with optaplanner i created a simple test project. I only have one Solution and one Entity class. The Entity has only one value between 0 and 9. There should only be odd numbers and the sum of all should be less then 10 (this are just some random constraints i came up with).
As Score i use a simple HardSoftScore. Here is the code:
public class TestScoreCalculator implements EasyScoreCalculator<TestSolution>{
#Override
public HardSoftScore calculateScore(TestSolution sol) {
int hardScore = 0;
int softScore = 0;
int valueSum = 0;
for (TestEntity entity : sol.getTestEntities()) {
valueSum += entity.getValue() == null? 0 : entity.getValue();
}
// hard Score
for (TestEntity entity : sol.getTestEntities()) {
if(entity.getValue() == null || entity.getValue() % 2 == 0)
hardScore -= 1; // constraint: only odd numbers
}
if(valueSum > 10)
hardScore -= 2; // constraint: sum should be less than 11
// soft Score
softScore = valueSum; // maximize
return HardSoftScore.valueOf(hardScore, softScore);
}
}
and this is my config file:
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<!-- Domain model configuration -->
<scanAnnotatedClasses/>
<!-- Score configuration -->
<scoreDirectorFactory>
<easyScoreCalculatorClass>score.TestScoreCalculator</easyScoreCalculatorClass>
</scoreDirectorFactory>
<!-- Optimization algorithms configuration -->
<termination>
<secondsSpentLimit>30</secondsSpentLimit>
</termination>
</solver>
for some reason OptaPlanner cant find a feasible solution. It terminates with LS step (161217), time spent (29910), score (-2hard/10soft), best score (-2hard/10soft)... and the solution 9 1 0 0.
So the hardScore is -2 because the two 0 are not odd. A possible solution would be 7 1 1 1 for example. Why is this ? This should be a really easy example ...
(when i set the Start values to 7 1 1 1 it terminates with this solution and a score of (0hard/10soft) how it should be)
Edit:
The Entity class
#PlanningEntity
public class TestEntity {
private Integer value;
#PlanningVariable(valueRangeProviderRefs = {"TestEntityValueRange"})
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
#ValueRangeProvider(id = "TestEntityValueRange")
public CountableValueRange<Integer> getStartPeriodRange() {
return ValueRangeFactory.createIntValueRange(0, 10);
}
}
The Solution class
#PlanningSolution
public class TestSolution {
private List<TestEntity> TestEntities;
private HardSoftScore score;
#PlanningEntityCollectionProperty
public List<TestEntity> getTestEntities() {
return TestEntities;
}
public void setTestEntities(List<TestEntity> testEntities) {
TestEntities = testEntities;
}
#PlanningScore
public HardSoftScore getScore() {
return score;
}
public void setScore(HardSoftScore score) {
this.score = score;
}
#Override
public String toString() {
String str = "";
for (TestEntity testEntity : TestEntities)
str += testEntity.getValue()+" ";
return str;
}
}
The Main Program class
public class Main {
public static final String SOLVER_CONFIG = "score/TestConfig.xml";
public static int printCount = 0;
public static void main(String[] args) {
init();
}
private static void init() {
SolverFactory<TestSolution> solverFactory = SolverFactory.createFromXmlResource(SOLVER_CONFIG);
Solver<TestSolution> solver = solverFactory.buildSolver();
TestSolution model = new TestSolution();
List<TestEntity> list = new ArrayList<TestEntity>();
// list.add(new TestEntity(){{setValue(7);}});
// list.add(new TestEntity(){{setValue(1);}});
// list.add(new TestEntity(){{setValue(1);}});
// list.add(new TestEntity(){{setValue(1);}});
for (int i = 0; i < 4; i++) {
list.add(new TestEntity());
}
model.setTestEntities(list);
// Solve the problem
TestSolution solution = solver.solve(model);
// Display the result
System.out.println(solution);
}
}
It gets stuck in a local optima because there is no move that takes 1 from entity and gives it to another entity. With a custom move you can add that.
These kind of moves only apply to numeric value ranges (which are rare, usually value ranges are a list of employees etc), but they should probably exist out of the box (feel free to create a jira for them).
Anyway, another way to get the good solution is to add <exhaustiveSearch/>, that bypassing local search and therefore the local optima. But that doesn't scale well.