0

As the title suggests I am building a web application using Spring Boot and Hibernate for my data access layer and the development is done on InteliJ IDEA 14.1.2.

My Knowledge

This is my first time using Spring Boot, Hibernate and InteliJ. I have built a few small apps to test Spring Boot and Hibernate, but the complexity difference between those and the one I am building now is a bit bigger.

Environment

Regarding my environment, in case it matters, I am running Windows 7 SP1 64bit, MySQL server 5.6.17, InteliJ 14.1.2 and Ubuntu Server 14.04 on a VirtualBox 4.3.26 VM hosting a Redis 3.0.1 server.

Purpose

The purpose of using the above technologies at this point in time is the storage and retrieval of different entities to a MySQL database (Redis is used only for session externalization and sharing among app instances). In other words, I am building my data access layer.

Database

My complete database schema can be found here:

https://dl.dropboxusercontent.com/u/49544122/so/DB.pdf

Source

My Spring Boot application is the following:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import se.domain.cvs.abstraction.dataaccess.AccountRepository;
import se.domain.cvs.domain.AccountEntity;

@SpringBootApplication
@EnableRedisHttpSession
public class Application implements CommandLineRunner {
    @Autowired
    AccountRepository repository;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... strings) throws Exception {     
        System.out.println("=======================================================");
        AccountEntity account = repository.findByEmail("r.franklin@companya.se");
        System.out.println("My name is " + account.getFirstName() + " " + account.getLastName());
        System.out.println("=======================================================");
    }
}

I am using CommandLineRunner interface just to test the bare data access layer without introducing REST endpoints yet.

My configuration is the following in YAML format:

...
# MySQL Database Configuration
spring.datasource:
    url: jdbc:mysql://localhost:3306/cvs
    username: cvs
    password: cvs
    driverClassName: com.mysql.jdbc.Driver

spring.jpa:
    database: MYSQL
    show-sql: true
    hibernate.ddl-auto: validate
    hibernate.naming-strategy: org.hibernate.cfg.DefaultNamingStrategy
    properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
...

The JPA entities are automatically generated with InteliJ and that is where the problems begin. Let's take for example the OrderEntity below (for the sake of brevity I omit some code):

...
@Entity
@Table(name = "order", schema = "", catalog = "cvs")
public class OrderEntity {
    ...
    private int invoiceId;
    ...
    private InvoiceEntity invoiceByInvoiceId;
    ...

    @Basic
    @Column(name = "InvoiceID", nullable = false, insertable = false, updatable = false)
    public int getInvoiceId() {
        return invoiceId;
    }

    public void setInvoiceId(int invoiceId) {
        this.invoiceId = invoiceId;
    }

    ...

    @ManyToOne
    @JoinColumn(name = "InvoiceID", referencedColumnName = "InvoiceID", nullable = false)
    public InvoiceEntity getInvoiceByInvoiceId() {
        return invoiceByInvoiceId;
    }

    public void setInvoiceByInvoiceId(InvoiceEntity invoiceByInvoiceId) {
        this.invoiceByInvoiceId = invoiceByInvoiceId;
    }
    ...
}

When trying to run the Spring Boot application I get the following error:

org.hibernate.MappingException: Repeated column in mapping for entity: OrderEntity column: invoiceId (should be mapped with insert="false" update="false")

After doing a little bit of research, I guess the problem is that the invoiceID now has two ways to be set, one through the setInvoiceID() setter and one through the InvoiceEntity object itself that the OrderEntity relates to, which could lead to an inconsistent state. As another user here puts it,

You would do that when the responsibility of creating/udpating the related entity in question isn't in the current entity.

See related post here: Please explain about: insertable=false, updatable=false

Setting the proposed values of the corresponding field (insertable and updateable) to false fixes the error.

My question here is why is this generated the wrong way? My change fixed the error, but I want to make sure that there is no errors in my SQL that lead InteliJ to generate this the wrong way. The complete SQL script can be found here http://pastebin.com/aDguqR1N.

Additionally, when generating the Entities, InteliJ requires a Hibernate config file which I guess Spring Boot generates on its own somewhere else (or uses Java based configuration). Whether I leave it there or delete it, it doesn't seem to affect the app at all. I guess the order taken by SB to read properties overrides it. Is it OK that I just remove it?

Thank you very much for your time and help in advance and sorry for this long post! :)

Community
  • 1
  • 1
  • TL;DR. Try to focus one ONE question. Post the *relevant* code, and the SQL query generated by Hibernate. You should never have an entity containing the ID of another entity. You should have ManyToOne or OneToOne associations instead. – JB Nizet May 09 '15 at 14:25
  • Hello! Thank you for your comment! It seems to me that indeed the ID properties are not needed at all, but what I want to know is why they got generated in the first place in case it's my SQL problematic. I also removed the second part of the post as you suggested. Thanks again! :) – George E. Kallergis May 09 '15 at 14:36
  • 1
    I don't know. I would never use a tool to autogenerate my entities. They're the heart and soul of the application, and I want to be in control. – JB Nizet May 09 '15 at 14:37

1 Answers1

0

my advice is to let Spring/Hibernate let generate your db schema for you ( everything including foreign keys and constraints can be generated by Spring.

For me the folloeing approach worked:

in the parent entity(in my case the TblUser):

@OneToMany(targetEntity=TblTracks.class,fetch=FetchType.EAGER,cascade=CascadeType.ALL,mappedBy="tbluser")
    private List<TblTracks> tbltracks= new ArrayList<TblTracks>();

where mappedBy points to the Tbluser Entity (private TblUser tbluser) of the child Entity

and in the child entity (in my case TblTracks) like

@ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name="idTblUser",nullable=false)
    private TblUser tbluser;
kamokaze
  • 7,142
  • 5
  • 33
  • 43