I have some puzzling issues when use Drools 5.5 final to compare String valued fields.
Essentially, I am trying to find if there are a pair of persons sharing the same name. The Person class looks like below:
public class Person {
private String name;
public String getName() { return name; }
public void setName(String n) { this.name = n; }
public Person(String name) { this.name = name;}
}
The rule that I try to trigger is :
rule "uniquePersonName"
when
$p1: Person($n1: name)
$p2: Person(this != $p1, name == $n1)
then
System.out.println("Duplicated person name found : " + $n1 + " " + $p2.getName());
end
But it never got triggered. However, if I change it to :
when
$p1: Person($n1: name)
$p2: Person(this != $p1, name != $n1)
The system works as expected, in other words, it finds all the pairs where the persons have different names.
After digging deeper, I found if I changed the name field to be of type Integer, the original rule worked fine. That made me think it was because of some bug with String comparison. So with the name field defined as the String type, I tried:
not (name == $n1)
(name == $n1)
name.toString() = $n1.toString()
name == $p1.getName()
Unfortunately, none of them worked.
Finally, the only way I could get it behave is to write the rule as:
when
$p1: Person($n1: name)
$p2: Person(name == $n1)
eval($p1!=$p2)
This made me think perhaps the problem is caused by a combination of how this works and how String comparison is done.
This is very basic feature and I would be surprised this is caused by a bug in Drools 5.5 final. But again, I could not see a way through. Can any of you help?
Thanks.
GW
It turns out this != $p1 transforms to !this.equals($p1) under the hood, and in my code I have an auto-generated equals method (code not shown in the original post), which totally change the default comparison behavior. After removing that equals method, all worked as expected.
Related
I'm trying to figure out how to use Android's Room library for implementing a prepopulated sqlite database in my app and I came across this Android tutorial. One of the lines (the one in the title) confuses me though, because in another tutorial (also by Android), this line isn't present. Why is this line of code present in the first tutorial but not the second? What is its purpose?
I ask this because my code (which I'm basing off the second tutorial) doesn't include this line and yet this post by a different user attempting to do something similar with a prepopulated database does include it.
Here is some of the code I have (each of the fields has a getter method which just returns this.thatfield'sname):
#Entity (tableName = "words")
public class Words {
#PrimaryKey
#NonNull
#ColumnInfo (name = "word_id")
private int wordId;
#ColumnInfo(name = "a_words")
private String aWords;
#ColumnInfo(name = "b_words")
private String bWords;
#ColumnInfo(name = "c_words")
private String cWords;
This code gives me a "Cannot find setter for field" but just changing the fields from public to private seems to solve that (not sure if this is the best way to solve this error, though).
Why is this line of code present in the first tutorial but not the second?
That line is an additional class constructor that takes 1 non-null String and sets the mWord member/variable to the provided String.
Without then you can only use myWord = new Word(); to instantiate a Word object and the value would be either the default value if provided or null.
With the additional constructor then you could use both
myWord = new Word();
or
myOtherWord = new Word("A Word");
So, in short it's provided an alternative way of constructing/instantiating a new Object of that Class.
Using your code then you could have, for example :-
#Entity(tableName = "words")
class Words {
#ColumnInfo(name = "word_id")
#PrimaryKey
private int wordId;
#ColumnInfo(name = "a_words")
String aWords;
#ColumnInfo(name = "b_words")
String bWords;
#ColumnInfo(name = "c_words")
String cWords;
public void setWordId(int wordId, String aWord, String bWords, String c) {
this.wordId = wordId;
this.aWords = aWord;
this.bWords = bWords;
this.cWords = c;
}
}
Note for demonstration the parameter names use 3 different standards, ideally you would stick to a single standard/convention for naming the parameters.
So now you could use the one constructor that expects 4 parameters e.g.
myWord = new Words(1,"Apple","Banana","Cherry");
which equates to
myWord = new Words();
myWord.wordId = 1;
myWord.aWords = "Apple;
myWord.bWords = "Banana";
myWord.cWords = "Cherry";
As you have specified a constructor, the default constructor is no longer usable.
What is its purpose?
As can be seen, additional constructors, can reduce the amount of coding, there use will also prompt for the values (hence the use of useful parameter names improves i.e. c as above is not very meaningful at all (although in conjunction with the other parameters if would be better than x))
I am new to Drools and have to compare value of 2 objects of same class.
i.e. let's say the class is
public class Person() {
public string Name;
}
So I want to compare person1's name and person2's name and throw an exception if they are equal.
It would be great if someone can give an example of the same.
That can easily be done with a rule similar to this:
rule 'Unique Names'
when
$p1: Person()
Person(this != $p1, name == $p1.name)
then
//error
end
I would discourage you to simply throw an exception in the then part of the rule though. It is a better idea to collect your results somewhere and then validate those results outside Drools.
I'm new to JPA . I'm using spring data and I want to create an entity "File" (a folder is a File ) . each File can have a parent (or not).
this is my table's columns :id ,name,path ,mtime ,parentid.
I used this in my entities
#ManyToOne
#JoinColumn(name = "parentid")
private File parent;
public File() {}
public File(String name, String path,File parent) {
this.name = name;
this.path=path;
this.parent=parent;
}
#Override
public String toString() {
return String.format(
"File[ id=%d,name='%s', path='%s',parentid=%d]",
id,name, path,parent.id);
}
in my test I did this :
fileRepository.save(new File("file1","file1",null));
File file1=fileRepository.findOne((long) 1);
fileRepository.save(new File("file2","file2",file1));
and it inserted the first line with parentid NULL and a second line with parentid 1 (the first file).( I confirmed that on phpmyadmin)
I wanted to show the lines so i did this :
for (Object element : Lists.newArrayList(fileRepository.findAll())) {
System.out.println(element);
}
but it doesn't work .
when I removed parentid from my toString() function I get the correct result :
File[ id=1,name='file1', path='file1']
File[ id=2,name='file2', path='file2']
I get the same problem if I add a new column of type Long and when one of the lines have a NULL value in that column .
how Can I fix that ?
Your toString() method "doesn't work" because you're trying to get the id of a null parent, which obviously causes a NullPointerException. You need to test if the parent is null before trying to get and print its ID. Reading the message, type and stack trace of the exception would have allowed you to find this out. Using a debugger as well.
A variable of type long can't hold null. Primitive types are not nullable. So you need to use the type java.lang.Long for this field.
Given the Play Framework 2.3 Computer Database sample application, I would like to practice adding a unique constraint on an attribute. Let's say I want the name attribute of the Computer class to be unique. I've tried to do this by adding a validate() function (and a getter) to Computer.java:
public List<ValidationError> validate() {
List<ValidationError> errors = new ArrayList<ValidationError>();
if(Computer.find.where().eq("name", getName()).findRowCount() != 0){
errors.add(new ValidationError("name", "Name must be unique. That value is already taken."));
}
return errors;
}
public String getName() {
return name;
}
This check works when creating new records in the database, however, this now causes a validation error when you update a Computer object but don't change the name. Is there a way to add a uniqueness constraint, similar to Rails? How can I validate uniqueness in Play?
Thanks!
UPDATE: see the answer by davide.
I ended up using the #Column(unique = true) constraint from the javax.persistence API. This doesn't generate an error in Play forms; instead, it throws a PersistenceException. Therefore I had to add change my controller to achieve the behavior I wanted. Both the create() and update() actions need a try/catch like this:
try {
computerForm.get().save();
} catch (PersistenceException pe) {
flash("error", "Please correct errors below.");
formData.reject("name", "Name conflict. Please choose a different name.");
return badRequest(createForm.render(computerForm));
}
UPDATE 2: each of the answers below is a possible solution
You need to exclude current entity from unique checking, i.e. like that:
if(Computer.find.where().eq("name", getName()).ne("id", getId()).findRowCount() != 0){
errors.add(new ValidationError("name", "Name must be unique."));
}
It will give you SQL query during update:
select count(*) from computer t0 where t0.name = 'Foo' and t0.id <> 123
And this during create:
select count(*) from computer t0 where t0.name = 'Foo' and t0.id is not null
P.S. ne() expression stands for Not Equal To and of course this approach assumes that your name field is Required
Edit: I sent you pull request with working solution, all you need is to add hidden field in your editForm like:
<input name="id" type="hidden" value='#computerForm("id").value'/>
Other thing is that you can simplify your model, i.e. don't need for getters for public fields.
I not sure if this answer your question, because I'm not familiar with Ruby syntax.
To "create a uniqueness constraint in the database" you can use the javax persistence API. Ebean will also recognize this.
To have a plain uniqueness constraint which involves a single field, you can use the #Column annotation:
#Entity
public class Computer extends Model {
...
#Column(unique = true)
public String name;
...
}
If you need some combination of fields to be unique, instead use the
#Table annotation
#Table(
uniqueConstraints=
#UniqueConstraint(columnNames={"name", "brand"})
)
#Entity
public class Computer extends Model {
...
public String name;
public String brand;
...
}
I hope it helps!
I have been working to setup Ormlite as the primary data access layer between a PostgreSQL database and Java application. Everything has been fairly straightforward, until I started messing with PostgreSQL's array types. In my case, I have two tables that make use of text[] array type. Following the documentation, I created a custom data persister as below:
public class StringArrayPersister extends StringType {
private static final StringArrayPersister singleTon = new StringArrayPersister();
private StringArrayPersister() {
super(SqlType.STRING, new Class<?>[]{String[].class});
}
public static StringArrayPersister getSingleton() {
return singleTon;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
String[] array = (String[]) javaObject;
if (array == null) {
return null;
} else {
String join = "";
for (String str : array) {
join += str +",";
}
return "'{" + join.substring(0,join.length() - 1) + "}'";
}
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
String string = (String) sqlArg;
if (string == null) {
return null;
} else {
return string.replaceAll("[{}]","").split(",");
}
}
}
And then in my business object implementation, I set up the persister class on the column likeso:
#DatabaseField(columnName = TAGS_FIELD, persisterClass = StringArrayPersister.class)
private String[] tags;
When ever I try inserting a new record with the Dao.create statement, I get an error message saying tags is of type text[], but got character varying... However, when querying existing records from the database, the business object (and text array) load just fine.
Any ideas?
UPDATE:
PostGresSQL 9.2. The exact error message:
Caused by: org.postgresql.util.PSQLException: ERROR: column "tags" is
of type text[] but expression is of type character varying Hint: You
will need to rewrite or cast the expression.
I've not used ormlite before (I generally use MyBatis), however, I believe the proximal issue is this code:
private StringArrayPersister() {
super(SqlType.STRING, new Class<?>[]{String[].class});
}
SqlType.String is mapped to varchar in SQL in the ormlite code, and so therefore I believe is the proximal cause of the error you're getting. See ormlite SQL Data Types info for more detail on that.
Try changing it to this:
private StringArrayPersister() {
super(SqlType.OTHER, new Class<?>[]{String[].class});
}
There may be other tweaks necessary as well to get it fully up and running, but that should get you passed this particular error with the varchar type mismatch.