Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConstraintViolationException: Validation failed for classes [....entities.WalletInfo] during persist

I work with Spring MVC RESTful app and get the ConstraintViolationException while persisting. The error message provided below,

Exception in thread " STARTING" javax.validation.ConstraintViolationException: Validation failed for classes [mobi.puut.entities.WalletInfo] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=id, rootBeanClass=class mobi.puut.entities.WalletInfo, messageTemplate='{javax.validation.constraints.NotNull.message}'}
    ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=currency, rootBeanClass=class mobi.puut.entities.WalletInfo, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140)
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
    at org.hibernate.action.internal.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:197)
    at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:75)
    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:626)
    at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:280)
    at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:261)
    at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:306)
    at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:318)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:275)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:780)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:765)
    at mobi.puut.database.WalletInfoDao.create(WalletInfoDao.java:62)
    at mobi.puut.database.WalletInfoDao$$FastClassBySpringCGLIB$$624213d.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
    at mobi.puut.database.WalletInfoDao$$EnhancerBySpringCGLIB$$a84b41b3.create(<generated>)
    at mobi.puut.services.WalletService.createWalletInfo(WalletService.java:273)
    at mobi.puut.services.WalletService.lambda$generateAddress$0(WalletService.java:97)
    at mobi.puut.controllers.WalletManager$1.lambda$onSetupCompleted$0(WalletManager.java:109)
    at java.lang.Iterable.forEach(Iterable.java:75)
    at java.util.Collections$SynchronizedCollection.forEach(Collections.java:2062)
    at mobi.puut.controllers.WalletManager$1.onSetupCompleted(WalletManager.java:109)
    at org.bitcoinj.kits.WalletAppKit.startUp(WalletAppKit.java:325)
    at com.google.common.util.concurrent.AbstractIdleService$2$1.run(AbstractIdleService.java:54)
    at com.google.common.util.concurrent.Callables$3.run(Callables.java:95)

The project structure is provided below,

enter image description here

The sample code directed to the error proviced below,

@Transactional(rollbackFor = Exception.class)
    public WalletInfo create(String name, String address) {

        // create the WalletInfo entity with provided name and address
        WalletInfo walletInfo = new WalletInfo();
        walletInfo.setAddress(address);
        walletInfo.setName(name);

        // persist the created instance into the database
        sessionFactory.getCurrentSession().persist(walletInfo);
        return walletInfo;
    }


protected WalletInfo createWalletInfo(final String walletName, final String address) {
        return walletInfoDao.create(walletName, address);
    }


public synchronized WalletInfo generateAddress(final String walletName, String currencyName) {

        // get the WalletInfo entity from the database with the wallet and the currency name
        WalletInfo walletInfo = walletInfoDao.getWalletInfoWithWalletNameAndCurrency(walletName, currencyName);

        // generate wallet, if the wallet is not
        // generated previously
        if (walletInfo == null) {

            if (genWalletMap.get(walletName) == null) {

                logger.info("Wallet name that we are workign on {}", walletName);

                final WalletManager walletManager = WalletManager.setupWallet(walletName);

                walletManager.addWalletSetupCompletedListener((wallet) -> {

                    Address address = wallet.currentReceiveAddress();
         /*at mobi.puut.services.WalletService.lambda$generateAddress$0(WalletService.java:97) */

                    WalletInfo newWallet = createWalletInfo(walletName, address.toString());

                    walletMangersMap.put(newWallet.getId(), walletManager);
                    genWalletMap.remove(walletName);
                });

                genWalletMap.put(walletName, walletManager);
            }
            return walletInfo;
        }
        return null;
    }



 private void setupWalletKit(final String walletId) {

        File directory = getWalletDirectory(walletId);

        // if the seed is not null, that means we are restoring from the backup
        bitcoin = new WalletAppKit(networkParameters, directory, WALLET_FILE_NAME) {

            @Override
            protected void onSetupCompleted() {

                // Don't make the user wait for confirmations
                // they're sending their own money anyway!!
                bitcoin.wallet().allowSpendingUnconfirmedTransactions();
                Wallet wallet = bitcoin.wallet();

                model.setWallet(wallet);

                /* lambda$onSetupCompleted$0(WalletManager.java:109) */
                setupCompletedListeners.forEach(listener -> listener.onSetupCompleted(wallet));
            }
        };

        // some code 
}

The WalletInfo entity provided,

@Entity
@Table(name = "wallet_info")
public class WalletInfo {

    @Id
    @Column(name = "id")
//    @NotNull
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Column(name = "name")
    private String name;

    @NotNull
    @Column(name = "address")
    private String address;

    @NotNull
    @Column(name = "currency")
    private String currency;

    public Long getId() {
        return id;
    }

    public WalletInfo(@NotNull String name, @NotNull String address, @NotNull String currency) {
        this.name = name;
        this.address = address;
        this.currency = currency;
    }

    public WalletInfo(@NotNull String name, @NotNull String address) {
        this.name = name;
        this.address = address;
    }

    public WalletInfo() {
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }


    public String getCurrency() {
        return currency;
    }

    public void setCurrency(String currency) {
        this.currency = currency;
    }
}

What is the issue here and how to solve it?

like image 590
Heisenberg Avatar asked Aug 20 '17 04:08

Heisenberg


2 Answers

The solution provided in the comment is correct, I just want to write it elaborately to help others, especially, who are using the Hibernate newly. The WalletInfo entity matches with the wallet_info table in the MySQL,

enter image description here

Notice that every entity is provided as non-null NN and hence, needs to be matched while persisting in the database.

The walletInfo entity class after the modification,

@Entity
@Table(name = "wallet_info")
public class WalletInfo {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Column(name = "name")
    private String name;

    @NotNull
    @Column(name = "address")
    private String address;

    @NotNull
    @Column(name = "currency")
    private String currency;

    // getters and setters 


   // constructors 

public WalletInfo(@NotNull String name, @NotNull String address, @NotNull String currency) {
        this.name = name;
        this.address = address;
        this.currency = currency;
    }

    public WalletInfo(@NotNull String name, @NotNull String address) {
        this.name = name;
        this.address = address;
    }

    public WalletInfo() {
    }
}

In the database layer, this was the method I was using to persist,

@Transactional(rollbackFor = Exception.class)
public WalletInfo create(String name, String currency, String address) {

    // create the WalletInfo entity with provided name and address
    WalletInfo walletInfo = new WalletInfo();
    walletInfo.setAddress(address);
    walletInfo.setName(name);
    walletInfo.setCurrency(currency);

    // persist the created instance into the database
    sessionFactory.getCurrentSession().persist(walletInfo);
    return walletInfo;
}

I just added the currecny column and hence, forget to add the walletInfo.setCurrency(currency) line which made the currency as null earlier and creates the error. I still can make the id null as the this is not an element in the constructor with @NotNull annotation and the entity parameter definition it was also not annotated with @NotNull

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

I hope this will help some people.

like image 157
Heisenberg Avatar answered Nov 05 '22 15:11

Heisenberg


In case you are working with lombok, you might have forgotten the @Data-Annotation over your entity class.

The reason why every attribute is null, while it shouldn't be, is that it's simply not possible for spring to write the data into your object, and therefore the exception occurs. No setters, no constructor => no data in your object, so the contents will always be null.

This is a very tricky thing, since you only get an exception when you validate your database entrys, without the Not-Null-Annotation, it just writes null into your fields, if that works out for all of them...

like image 1
monamona Avatar answered Nov 05 '22 15:11

monamona