0

So, I'm making this webapp using Hibernate 5 and SpringMVC 4. I can't, for some reason, insert an entity with a OneToMany relation. Before I explain anything first I want to say that i tried many of the solutions posted here and other forums and neither worked for me... I don't know if I'm doing something wrong while trying to solve the issue.

Here is my database: My Database

Now, i recently added the phone table. Before this, I could add a profile object and it would cascade the inserts of the address and user entities on the same transaction. Now, with the addition of the phone entity to the code the persist operation keeps throwing me

Column 'ProfileId' cannot be null

All classes have their respective Setters and Getters... but to keep the code short, I removed them.

@Entity
@Table(name = "profile")
public class Profile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @NotEmpty
    @Size(max = 100)
    @Column(name = "FirstName", nullable = false, unique = false, length = 100)
    private String firstName;

    @NotEmpty
    @Size(max = 100)
    @Column(name = "LastName", nullable = false, unique = false, length = 100)
    private String lastName;

    @NotNull
    @DateTimeFormat(pattern = "MM/dd/yyyy")
    @Column(name = "BirthDate", nullable = false, unique = false)
    private Timestamp birthDate;

    @Valid
    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "UserId")
    private User user;

    @Valid
    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "AddressId")
    private Address address;

    @Valid
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "profile", cascade = CascadeType.ALL)
    private List<Phone> phones;
}

_

@Entity
@Table(name = "Phone")
public class Phone {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @NotEmpty
    @Size(max = 50)
    @Column(name = "PhoneNumber", nullable = false, unique = false, length = 50)
    private String phoneNumber;

    @Valid
    @ManyToOne(cascade = CascadeType.ALL, optional = false)
    @JoinColumn(name = "ProfileId", nullable = false)
    private Profile profile;
}

_

@Entity
@Table(name = "\"User\"", uniqueConstraints = { @UniqueConstraint(columnNames = "Username") })
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "Id", unique = true, nullable = false)
    private long id;

    @Column(name = "Username", nullable = false, unique = true, length = 80)
    private String username;

    @Column(name = "Password", nullable = false, length = 128)
    private String password;

    @Valid
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
    private Profile profile;
}

_

@Entity
@Table(name = "Address")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "FullAddress", nullable = false, unique = false, length = 100)
    private String fullAddress;

    @Column(name = "City", nullable = false, unique = false, length = 100)
    private String city;

    @Column(name = "PostalCode", nullable = false, unique = false, length = 100)
    private String postalCode;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "address", cascade = CascadeType.ALL)
    private Profile profile;
}

Between the solutions i found for similar cases than this was:

  • To change the ProfileId column of the Phone table to make it allow null values, but keep the nullable = false on the JoinColumn annotation. The result for this was that it actually inserted the all the entities, but the ProfileId was also saved as null.
  • Changed the cascade types... Still the same error.
  • Someone said that EntityManager contexts that were colliding.. this is not my case.
  • Set the JoinColumn annotation attribute nullable to false. Already did, is still on the code and is not working either.
  • Set the Cascade type on both sides of the relationship. Also did, is also still on the code and is not working.
  • Set the ManyToOne relationship attribute optional to false. Also not working.

Being as new as I'm in this.. what i understand is that the ProfileId is receiving null because for some reason, after the insert of the Profile table, the generated id is not yet set on the profile object, which causes that the phone insert fails. But i have no idea how to solve.

In case you need to know how I am persisting the objects...

sessionFactory.getCurrentSession().persist(profile);

And the sessionFactory is autowired. The profile object has something like:

{"phones":[{"phoneNumber":"123456789"}],"user":{"username":"Nameeeeee@alksjd.com","password":"123123123"},"firstName":"Nameeeeee","lastName":"Nameeeeee","birthDate":"02/05/2016", "address":{"fullAddress":"laksjdlkas","city":"alksjdlkasjd","postalCode":"101010"}}

And lastly the full error:

Hibernate: 
    insert 
    into
        Address
        (FullAddress, City, PostalCode) 
    values
        (?, ?, ?)
Hibernate: 
    select
        last_insert_id()
Hibernate: 
    insert 
    into
        `User` (
            Password, Username
        ) 
    values
        (?, ?)
Hibernate: 
    select
        last_insert_id()
Hibernate: 
    insert 
    into
        Profile
        (AddressId, BirthDate, FirstName, LastName, UserId) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    select
        last_insert_id()
Hibernate: 
    insert 
    into
        Phone
        (PhoneNumber, ProfileId) 
    values
        (?, ?)
WARN  SqlExceptionHelper::logExceptions:127 - SQL Error: 1048, SQLState: 23000
ERROR SqlExceptionHelper::logExceptions:129 - Column 'ProfileId' cannot be null

Adding Requested Code.

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = { "com.configuration" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {

    final static Logger logger = Logger.getLogger(HibernateConfiguration.class);
    @Autowired
    private Environment environment;

    @Bean
    public LocalSessionFactoryBean sessionFactoryBean() {
        logger.info("Creating LocalSessionFactoryBean...");
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource());
        sessionFactoryBean.setPackagesToScan(new String[] { "com.model" });
        sessionFactoryBean.setHibernateProperties(hibernateProperties());
        return sessionFactoryBean;
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
        dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
        dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
        dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
        return dataSource;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(sessionFactory);
        return transactionManager;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
        properties.put("hibernate.temp.use_jdbc_metadata_defaults",
                environment.getRequiredProperty("hibernate.temp.use_jdbc_metadata_defaults"));
    }
dbncourt
  • 560
  • 4
  • 10
  • 27

1 Answers1

1

Somewhere in your code you should do this

Phone phone = new Phone();
//... set phone vars
phone.setProfile(profile);

sessionFactory.getCurrentSession().persist(profile);
yugo
  • 188
  • 7
  • Thanks for the answer. But i still get the exact same error. – dbncourt Feb 06 '16 at 03:10
  • I have a doubt.. did you meant sessionFactory.getCurrentSession().persist(phone);? – dbncourt Feb 06 '16 at 04:16
  • I meant profile . Since you are appending phones to a profile , they should be persisted along with it. Would you post the code where you create the Profile and Phone objects and set the references? – yugo Feb 06 '16 at 04:23
  • Ok, it worked.. now i dont understand why! The Profile object is comming from the front-end in a Json. With the same data posted above, which already includes the Phone. Why do i have to re-set it? And thank you very much for your help, sorry i made you waste your time. – dbncourt Feb 06 '16 at 04:40
  • Ok, never mind! i saw the difference! Thank you very much for your time! – dbncourt Feb 06 '16 at 04:47
  • Here we have circular dependency of Phone & profile classes. I think setting up profile completed the circular dependency of parent child. See the article : https://stackoverflow.com/a/10957727/4860775 – ASharma7 Dec 24 '18 at 00:00
  • & this one as well https://stackoverflow.com/questions/2584521/in-a-bidirectional-jpa-onetomany-manytoone-association-what-is-meant-by-the-in?rq=1 – ASharma7 Dec 24 '18 at 00:30