first of all thanks for everyone who tries to help me with this: I'm trying to validate a list with at least one element, if I check using
Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR.validate(anObj);
The constraint works fine, but when I try to persist, the object with one element pass again and the list is null...
This is the part of the class with the list:
public class Team implements Serializable {
//...
#AtLeastOneNotNull
#ManyToMany
#JoinTable(
name="teams_players"
, joinColumns={
#JoinColumn(name="id_team")
}
, inverseJoinColumns={
#JoinColumn(name="id_player")
}
)
private List<Player> players;
//...
}
The validator:
public class AtLeastOneNotNullValidator implements ConstraintValidator<AtLeastOneNotNull, List<?>> {
#Override
public void initialize(AtLeastOneNotNull constraint) {
}
#Override
public boolean isValid(List<?> aCollection, ConstraintValidatorContext aConstraintValidatorContext) {
if(aCollection == null || aCollection.isEmpty())
return false;
else
return true;
}
}
Annotation:
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = AtLeastOneNotNullValidator.class)
public #interface AtLeastOneNotNull {
String message() default "{[ERROR] Collection must have at least one element}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Test class:
public class TeamDAOTest {
private static Validator VALIDATOR;
TeamTestHelper teamTestHelper = null;
TeamDAO teamDaoTest = null;
#Before
public void initTestClass() {
teamDaoTest = new TeamDAO();
teamTestHelper = new TeamTestHelper();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
VALIDATOR = factory.getValidator();
}
#After
public void endTestClass() {
}
#Test
public void savingRightTeam() {
Team testTeam = teamTestHelper.getTeamOK();
// Set<ConstraintViolation<Team>> constraintViolations = VALIDATOR.validate(testTeam);
//
// assertEquals(0, constraintViolations.size());
//
assertEquals("Inserting right Team", true, teamDaoTest.updateEntity(testTeam));
}
#Test
public void savingWrongTeam() {
Team testTeam = teamTestHelper.getTeamKO_WithoutPlayers();
// Set<ConstraintViolation<Team>> constraintViolations = VALIDATOR.validate(testTeam);
//
// assertEquals(1, constraintViolations.size());
assertEquals("Inserting empty Team", false, teamDaoTest.updateEntity(testTeam));
}
}
And finally, this is what happens when I run this
First of all, you can see the Team created with the list with one element.
Note: The helper creates a player and persist it first, then adds to a team and return it, and this is the Team that we can see.
In a second step, when it's trying to persist the element and gets through the validator, the list is empty... ¿why?
And finally, of course, with a null list, the merge method throws an exception:
Any idea of what could be happening? I don't know why this happens, as you can see, I commented the other lines, the test pass ok, but not when I try to update, it seems as another object, or as the object instantiates again
Thanks again to everyone
EDIT:
The getPlayer() and addPlayer() methods in Team object:
public List<Player> getPlayers() {
if(this.players == null)
this.players = new ArrayList<Player>();
return this.players;
}
public boolean addPlayer(Player aPlayer){
if(!getPlayers().contains(aPlayer)){
this.players.add(aPlayer);
return true;
}
else return false;
}
Edit 2: DAO, as you can see, by now I don't need more specific methods in TeamDAO
public class TeamDAO extends AbstractDAOLayer<Team> {
}
UpdateEntity method in AbstractDAOLayer
public boolean updateEntity(T anObject){
try{
this.beginTransaction();
this.entityManager.merge(anObject);
this.finishtTransaction();
return true;
}
catch(Exception e){
return false;
}
}
Please check this. The problem has nothing to do with BV but rather with jpa merge semantic with many-to-many relationship.
Related
I'm currently studying building API's with Spring. I'm working with Spring Validator to validate my input. Here it is my custom validator:
public class NewHoldValidator implements Validator {
private EntityManager manager;
public NewHoldValidator(EntityManager manager) {
this.manager = manager;
}
#Override
public boolean supports(Class<?> clazz) {
return NewHoldRequest.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
if (errors.hasErrors()) {
return;
}
NewHoldRequest request = (NewHoldRequest) target;
Patron patron = manager.find(Patron.class, request.patronId);
BookInstance bookInstance = manager.find(BookInstance.class, request.bookInstanceId);
Assert.state(patron != null, "Patron does not exists.");
Assert.state(bookInstance != null, "Book instance does not exists.");
if (!bookInstance.acceptToBeHoldTo(patron)) {
errors.reject(null, "This book instance cannot be hold to this patron");
}
if (!request.hasDaysHold()) {
if (!patron.researcher()) {
errors.rejectValue("daysHold", null, "You need to pass a daysHold attribute");
}
}
}
}
And here is my NewHoldRequest class:
public class NewHoldRequest {
#NotNull
public final Long patronId;
#NotNull
public final Long bookInstanceId;
#Positive
#Max(60)
public final Integer daysHold;
public NewHoldRequest(#NotNull Long patronId, #NotNull Long bookInstanceId, #Positive #Max(60) Integer daysHold) {
this.patronId = patronId;
this.bookInstanceId = bookInstanceId;
this.daysHold = daysHold;
}
#Override
public String toString() {
return "NewHoldRequest{" + "patronId=" + patronId + ", bookId=" + bookInstanceId + ", daysHold=" + daysHold + '}';
}
public boolean hasDaysHold() {
return this.daysHold != null;
}
Even if my field "daysHold" is public I still need to create a getter to it so Spring can show the rejected error properly, otherwise, it will throw a NotReadablePropertyException. Is there a way to define that Spring can reject public fields without getters or I will need to add accessor methods to all fields I want to reject?
Here is the message that shows up when my validation is triggered.
org.springframework.beans.NotReadablePropertyException: Invalid property 'daysHold' of bean class [com.api.library.usecases.newhold.NewHoldRequest]: Bean property 'daysHold' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
I'll advise you to use the interface ConstraintValidator. It's a generic's interface, without casting and other problems.
You should create a custom constraint annotation for you it's #NewHoldValid after that implement CustomValidator interface:
#Component
public class NewHoldValidator implements ConstraintValidator<NewHoldValid, NewHoldRequest> {
#Override
public boolean isValid(NewHoldRequest value, ConstraintValidatorContext context) {
if(ANY_CONDITION){
return true;
}else {
return false;
}
}
After that use #NewHoldValid annotation above your NewHoldRequest.
If you'll want to set an error message text which different from default use that:
context.buildConstraintViolationWithTemplate("Your error message").addConstraintViolation();
I want to be able to annotate a class with a list of 2 types of annotations which are related in function, but are completely different in arguments. The order of this list matters though. I have tried looking for this already, but was unable to find any references to this (I was not sure what to call this).
Edit:
What I want to be able to do in the end:
//place holder for example (Abstract)
public #interface A {
}
#Target(PARAMETER)
public #interface B extends A {
//Gets stuff
Class type();
int key();
}
#Target(PARAMETER)
public #interface FlaggedListOfA extends A {
//List of A
A[] set();
}
//Goal is to have annotation that does this
#Target(METHOD)
public #interface ImportantFunc {
A[] dataForA() default {};
String[] names();
int property() default 0;
//etc.
}
//End goal:
public class SomeImportantClass {
#ImportantFunc(dataForA = {#B(...), #B(...}, ...)
public void doStuff() {
}
//So I can have an end goal of this (Order matters, may contain repeats,
//and has unknown length!)
#ImportantFunc(dataForA = {#B(...), #FlaggedListOfA(#B(...), #B(...))}, ...)
public void doStuffB() {
}
#ImportantFunc(dataForA = {#FlaggedListOfA(#B(...)), #FlaggedListOfA(#B(...), #B(...))}, ...)
public void doStuffC() {
}
#ImportantFunc(dataForA = {#FlaggedListOfA(#B(...), #FlaggedListOfA(#B(...), #B(...))), #B(...)}, ...)
public void doStuffD() {
}
}
Reflections to get all uses of ImportantFunc (Ex: 100 uses of it) in package and uses this data to choose which function to use. The annotation is to help with the reflection since once it gets the data from #ImportantFunc, it then converts it to input for a library which does the actual choosing of which function to execute (this is internal and can not be modified). This could also be achieved with much longer and more annoying ways, but I was hoping to use annotations to simplify the process of defining all of these functions.
Edit:
Another way this could be solved is finding a way to group two annotations together.
Being able to do this would not be completely ideal, but would definitely make this much more workable:
public #interface Example {
AnyTypeOfAnnotation[] set();
}
One kludgy way to do this is to actually make A be a union of B and C. This means it has all the fields of both B and C, but you only ever use it as either a B or a C.
Here's a working example.
import java.lang.annotation.*;
enum NoType {;}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface A {
Class<?> data() default NoType.class; // field from B
int dataA() default 0; // field from C
String dataB() default ""; // field from C
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface D {
A[] value() default {};
}
class Foo {}
class Bar {}
class Example {
#D({
#A(data = Bar.class),
#A(dataA = 5, dataB = "Bla"),
#A(data = Foo.class)
})
public static void main(String[] args) throws Exception {
for (A a : Example.class.getMethod("main", String[].class)
.getAnnotation(D.class).value()) {
if (a.data() != NoType.class) {
System.out.printf("B(%s)%n", a.data());
} else {
System.out.printf("C(dataA = %d, dataB = \"%s\")%n",
a.dataA(), a.dataB());
}
}
}
}
The output of that program is:
B(class Bar)
C(dataA = 5, dataB = "Bla")
B(class Foo)
Of course, it's not a very pretty solution, but it does work.
Not sure this would be sufficient for your use case:
public #interface A {
}
public #interface B extends A {
//Gets stuff
Class data();
}
public #interface C extends A {
//Gets different stuff related to same goal
int dataA();
String dataB();
}
public #interface D {
Class<? extends A>[] order();
}
#B(Bar.class)
#C(dataA = 5, dataB = "Bla")
#D(order = {B.class, C.class})
public class SomeImportantClass {
}
This method uses D annotation as a mean to retain annotation order. The bad part is that you cannot add multiple annotations of the same type.
There is another method that makes A, B and C into normal classes.
public abstract class AnnotationAttribute {
public abstract Class<?>[] getDataTypes();
public abstract Object[] getData();
}
public class B extends AnnotationAttribute {
#Override public Class<?>[] getDataTypes() {
return new Class<?>[] {Foo.class, Bar.class};
}
#Override public Object[] getData() {
return new Object[] {new Foo(), new Bar()};
}
}
public #interface D {
Class<? extends AnnotationAttribute>[] data() default {};
}
#D(data = {B.class});
public class Test {
}
This method requires you to create one class for one concrete attribute type. This is because annotations have to be compile-time constant, and referencing via Class requires you to define the class out in code.
I do have two entities that relate each other via a OneToMany-Relation.
Entity 1 is named "Change" and looks like the following
public class Change {
String attribute1;
#Column(name="\"ATTRIBUTE1\"")
public void getAttribute1() {
return this.attribute1;
}
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
// and 7 more of these....
List<ChangeTask> relatedChangeTasks = new ArrayList<ChangeTask>();
#OneToMany(cascade={PERSIST, MERGE, REFRESH}
#JoinTable(name="CHANGE_CHANGETASK", joinColumns={#JoinColumn(name="CHANGE_ID")}, inverseJoinColumns={#JoinColumn(name="CHANGETASK_ID")})
#JoinColumn(name="\"relatedChangeTask_ID\"" )
public List<ChangeTask> getRelatedChangeTasks() {
return this.relatedChangeTasks;
}
public void setRelatedChangeTasks(List<ChangeTask> relatedChangeTasks) {
this.relatedChangeTasks = relatedChangeTasks;
}
}
Entity 2 is named ChangeTask and extends Change.
public class ChangeTask extends Change {
// some additional attributes...
}
Persisting a new or existing Change record with one ChangeTask added to the "relatedChangeTask" list works just perfect.
Now I have to change the annotation of the 8 attributes from Default to #Lob, so Change now looks like this:
public class Change {
String attribute1;
#Lob
#Column(name="\"ATTRIBUTE1\"")
#Basic(fetch=EAGER)
public String getAttribute1() {
if(fieldHandler != null) {
return (java.lang.String) fieldHandler.readObject(this, "attribute1", attribute1);
}
return attribute1;
}
public void setAttribute1(String attribute1) {
if(fieldHandler != null) {
this.attribute1= (java.lang.String) fieldHandler.writeObject(this, "attribute1", this.attribute1, attribute1);
return;
}
this.attribute1= attribute1;
}
// and 7 more of these....
List<ChangeTask> relatedChangeTasks = new ArrayList<ChangeTask>();
#OneToMany(cascade={PERSIST, MERGE, REFRESH}
#JoinTable(name="CHANGE_CHANGETASK", joinColumns={#JoinColumn(name="CHANGE_ID")}, inverseJoinColumns={#JoinColumn(name="CHANGETASK_ID")})
#JoinColumn(name="\"relatedChangeTask_ID\"" )
public List<ChangeTask> getRelatedChangeTasks() {
return this.relatedChangeTasks;
}
public void setRelatedChangeTasks(List<ChangeTask> relatedChangeTasks) {
this.relatedChangeTasks = relatedChangeTasks;
}
}
Now, when I try to add a given ChangeTask to a Change the persist operation does not fail. But at the end of the Transaction the relation has not been persisted, meaning the relation-table "CHANGE_CHANGETASK" remains empty. When I debug through the whole process, I can see that the list contains one entry before "entityManager.merge()" operation and it still contains one entry after the merge. But it never arrives at the database.
Does anybody have an idea what I'm doing wrong here? As strange as it may sound, it must be something related with the #Lob annotations. As soon as I remove those again from the entity everything works fine.
Thanks in advance.
You wrote
public void getAttribute1() {
That can't be right. I think you mean
public String getAttribute1() {
Additionally you have annotated the setter:
#Column(name="\"ATTRIBUTE1\"")
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
You have to annotage either the field or the getter.
How to write (type level annotation) custom annotation to allow a choice validation (exactly one of a number of properties has to be not null)?
Even if this "question" is very broad I will give an answer as I have exactly the needed piece of code here:
#Target(ElementType.TYPE)
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = ChoiceValidator.class)
public #interface Choice {
String[] value();
boolean multiple() default false;
String message() default "{com.stackoverflow.validation.Choice.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Here a validator using a bean property access:
public class ChoiceValidator implements ConstraintValidator<Choice, Object> {
private String[] properties;
private boolean allowMultiple;
#Override
public void initialize(Choice constraintAnnotation) {
if (constraintAnnotation.value().length < 2) {
throw new IllegalArgumentException("at least two properties needed to make a choice");
}
properties = constraintAnnotation.value();
allowMultiple = constraintAnnotation.multiple();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try {
BeanInfo info = getBeanInfo(value.getClass());
long notNull = Stream.of(properties)
.map(property -> Stream.of(info.getPropertyDescriptors())
.filter(desr -> desr.getName().equals(property))
.findAny()
.orElse(null)
)
.map(prop -> getProperty(prop, value))
.filter(Objects::nonNull)
.count();
return allowMultiple ? notNull != 0 : notNull == 1;
} catch (IntrospectionException noBean) {
return false;
}
}
private Object getProperty(PropertyDescriptor prop, Object bean) {
try {
return prop.getReadMethod() == null ? null : prop.getReadMethod().invoke(bean);
} catch (ReflectiveOperationException noAccess) {
return null;
}
}
}
A typical usage can look like this (lombok annotation to generate getters and setters):
#Data
#Choice({"one", "two"})
class OneOf {
private String one;
private String two;
private String whatever;
}
#Data
#Choice(value = {"one", "two"}, multiple = true)
class AnyOf {
private String one;
private String two;
}
But to clarify: Stackoverflow is a QA community for developers to exchange knowledge. It is not a place to ask "Can you please code this for me for free?". All valuable contributors at least try to solve problems first and come with detailed questions afterwards. People answering questions spend their spare time and are not payed for. Please show respect by asking for specific problems and showing own efforts in the future.
I need something that seems not so specific but anyway I was unable to come up with nice and sophisticated solution.
Say I have very simple hibernate/jpa entity:
#Entity(name="entity")
public class Type {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique = true, nullable = false)
private String name;
#Column(unique = false, nullable = false)
private boolean defaultType;
}
What i need is to somehow annotate defaultType field so only (and exactly) one persisted entity have this value as true. When new entity get persisted with this defaultType as true, the old one (with defaultType=true) entity has to be altered and its defaultType value changed to false. Also if any entity get changed (its defaultType got changed to true), same rule should apply.
As far I know this can be achieved inside business logic (e.g. in DAO layer), with DB trigger or with hibernates interceptor or event (If there is another way, please let me know). I tried with DAO solution but it's kind of bad solution because it can be bypassed and it is really clumsy for such simple operation. DB triggers can not be added with hibernate/jpa annotations (if I am not mistaken) and i am not sure how to make this functionality with hibernate interceptors/events.
So, what is best solution for this problem?
You need use Callback method in JPA, for example PreUpdate or PostUpdate, for instance:
#Entity
#EntityListeners(com.acme.AlertMonitor.class) // set callback method in another class
public class Account {
Long accountId;
Integer balance;
boolean preferred;
#Id
public Long getAccountId() { ... }
...
public Integer getBalance() { ... }
...
#Transient
public boolean isPreferred() { ... }
...
public void deposit(Integer amount) { ... }
public Integer withdraw(Integer amount) throws NSFException {... }
#PreUpdate // callback method in some class
protected void validateCreate() {
if (getBalance() < MIN_REQUIRED_BALANCE)
throw new AccountException("Insufficient balance to open an
account");
}
#PostUpdate // callback method in some class
protected void adjustPreferredStatus() {
preferred =
(getBalance() >= AccountManager.getPreferredStatusLevel());
}
}
// callback method in another class
public class AlertMonitor {
#PreUpdate // callback method in another class
public void updateAccountAlert(Account acct) {
Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
}
}
Update: About your question, If I undestand what you want, this code may help you:
#Entity(name="entity")
#EntityListeners(com.yourpackage.TypeListner.class)
public class Type {
...
#Column(unique = false, nullable = false)
private boolean defaultType;
}
public class TypeListner {
pivate static Type objectWithTrue = null;
public void init() { // call this method when application is started
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(type.getDefaultType()) {
objectWithTrue = type;
}
}
}
private void changeDefaultType(Type changed) {
if(changed.getDefaultType()) {
if(changed != objectWithTrue && objectWithTrue != null) {
objectWithTrue.setDefaultType(false);
}
objectWithTrue = changed;
}
}
#PostPresist
public void newType(Type changed) {
changeDefaultType(changed);
}
#PostUpdate
public void updateType(Type changed) {
changeDefaultType(changed);
}
#PreRemove
public void removeType(Type changed) {
if(changed.getDefaultType() && objectWithTrue == changed) {
objectWithTrue = null;
}
}
OR
You can use listner #PreUpdate and #PrePresist and every times overwrite all Type objects without store any variable (it isn't so good for perfomance then first example, but more reliable):
#PreUpdate
void updateType(Type changed) {
if(changed.getDefaultType()
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(changed != type && type.getDefaultType()) {
type.setDefaultType(false);
}
}
}
}