javaspringspring-bootjpa

The SQL Table name gets automatically changed during PostMapping


I started learning spring boot recently and facing some problem with the PostMapping in my rest controller. While the postmapping endpoint is exceuted the SQL table automatically changes from "employees" to "employee_seq" and it throws an error stating table does not exist.

This is my Employee entity class and I have mentioned the table name "employee"

@Entity
@Table(name = "employee")
public class Employee {

    // define fields
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name  = "id")
    private int id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

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

    // define constructors
    public Employee() {
    }
    public Employee(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    // define getters / setters
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }

    // define a toString() to display an employee
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

This is the DAO Implementation class of the interface EmployeeDAO

@Repository
public class EmployeeDAOJpaImpl implements EmployeeDao{

    //define fields for entity manager
    private EntityManager entityManager;

    // setup constructor injection
    @Autowired
    public EmployeeDAOJpaImpl(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public List<Employee> findAll() {
        // create a query
        TypedQuery<Employee> query = entityManager.createQuery("from Employee", Employee.class);
        // execute the query and get the result list
        List<Employee> employees = query.getResultList();
        // return the result
        return employees;
    }

    @Override
    public Employee findById(int id) {
        return entityManager.find(Employee.class, id);
    }

    @Override
    public Employee save(Employee emp) {
        Employee dbEmp = entityManager.merge(emp);
        return dbEmp;
    }

    @Override
    public void deleteById(int id) {
        Employee emp = entityManager.find(Employee.class, id);
        entityManager.remove(emp);
    }
}

The Service Implementation class of the interface EmployeeService

@Service
public class EmployeeServiceImpl implements EmployeeService{

    private EmployeeDao employeeDao;

    @Autowired
    public EmployeeServiceImpl(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }
    @Override
    public List<Employee> findAll() {
        return employeeDao.findAll();
    }

    @Override
    @Transactional
    public Employee findById(int id) {
        return  employeeDao.findById(id);
    }

    @Transactional
    @Override
    public Employee save(Employee emp) {
        return employeeDao.save(emp);
    }

    @Transactional
    @Override
    public void deleteById(int id) {
        employeeDao.deleteById(id);
    }
}

And here is the REST controller

@RestController
@RequestMapping("/api")
public class EmployeeRestController {

    /*
    The rest controller is only concerned with the EmployeeService and nothing else.
     */
    // using the service view to get the final view
    private EmployeeService employeeService;

    @Autowired
    public EmployeeRestController (EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    // expose the "/employees" and return the list of employees
    @GetMapping("/employees")
    public List<Employee> findAll() {
        return employeeService.findAll();
    }

    @GetMapping("/employees/{empId}")
    public Employee findById(@PathVariable int empId) {
        Employee emp = employeeService.findById(empId);
        if(emp == null) throw new RuntimeException("Employee id not found" + empId);
        return emp;
    }

    @PostMapping("/employees")
    public Employee addEmployee(@RequestBody Employee emp) {
        emp.setId(0);
        Employee dbEmp = employeeService.save(emp);
        return dbEmp;
    }
}

I created a DAO layer and a Service layer, and all the transactional operations are performed from the Service layer. In the post mapping I am performing a "addEmployee" and "updateEmployee" functionalities using the "merge()" fucntion from the ResponseEntity. Please help me to resolve this.

I passed a JSON object to the POST endpoint and it should return me the newly created or updated record of the Employee. Instead it threw an error stating SQLException , Table does not exist as the code was trying to access "employee_seq" but I never mentioned this table.


Solution

  • First of all: use an Integer (or, better, a Long) for the id field, never use a primitive value because ids managing by the persistence layer. And following this, remove the emp.setId(0); line, since it is supposed the db will provide the identifier value, during persistence.

    I also noted that the save method of the DAO calls the merge one of the PersistenceManager, instead of the persist (that should be used for inserts), why? If you are using that because of the returned object, you should write like this, instead:

    @Override
    public Employee save(Employee emp) {
        entityManager.persist(emp);
        return emp;
    }
    

    Because the very emp reference object id will be set by the persistence layer. Please read the Jpa documentation to understand the difference between persist and merge. Moreover, since you are using Spring Boot, I suggest to use the JpaRepository interface and the Spring Data Jpa approach, instead of re-implement your own DAO.

    Which db are you using, MySQL, Postgres... ? In the first case, if you are using an auto-increment management for setting id value, you may need to set the annotation to @GeneratedValue(strategy = GenerationType.IDENTITY) instead of AUTO. On the other way, if you are using a sequence, you have to specify it in the annotations.