dbncourt
dbncourt

Reputation: 580

Hibernate - OneToMany Bidirectional Insert resulting in null

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:

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":"[email protected]","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"));
    }

Upvotes: 0

Views: 1805

Answers (1)

yugo
yugo

Reputation: 198

Somewhere in your code you should do this

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

sessionFactory.getCurrentSession().persist(profile);

Upvotes: 1

Related Questions