Skip to content

employee-dev-13-BE-add-ticket-table-back #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions springEmployeeSystem/TICKET_SYSTEM_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# 🎫 Ticket System - Database Auto-Creation Setup

This document explains the backend configuration for automatic ticket table creation in the Employee Management System.

## 🚀 **What's Been Fixed**

### ✅ **Enhanced Ticket Entity (`Ticket.java`)**
- **Fixed LONGTEXT Column**: Changed from `TEXT` to `LONGTEXT` for better MySQL compatibility
- **Added Timestamps**: Automatic `createdAt` and `updatedAt` tracking
- **Removed Manual SQL**: No more manual `ALTER TABLE` commands needed
- **Better Annotations**: Proper JPA annotations for robust table creation

### ✅ **Improved Application Configuration (`application.properties`)**
- **MySQL 8 Dialect**: Updated to use MySQL 8 dialect for better compatibility
- **Enhanced Logging**: Added detailed SQL logging for debugging
- **Auto DDL Update**: Proper Hibernate DDL configuration
- **Formatted SQL**: Better readable SQL output in logs

### ✅ **Database Configuration Class (`DatabaseConfig.java`)**
- **Guaranteed Table Creation**: Programmatic table creation with proper indexes
- **Error Handling**: Comprehensive error handling and logging
- **Table Verification**: Automatic table structure verification on startup
- **Performance Indexes**: Added indexes for better query performance

### ✅ **Sample Data Initialization (`data.sql`)**
- **Schema Data**: Pre-populated ticket statuses and tags
- **Sample Tickets**: Example tickets for testing
- **Safe Inserts**: Uses `INSERT IGNORE` to prevent duplicates

## 🛠️ **New Features Added**

### 📅 **Automatic Timestamps**
```java
@CreationTimestamp
private LocalDateTime createdAt;

@UpdateTimestamp
private LocalDateTime updatedAt;
```

### 🗃️ **Performance Indexes**
- `idx_user_id` - Fast queries by user
- `idx_assigned_to` - Fast queries by assignee
- `idx_status` - Fast status filtering
- `idx_created_at` - Time-based queries

### 🔧 **Enhanced Service Layer**
- **Proper Updates**: No more delete+insert operations
- **Timestamp Preservation**: Maintains creation time during updates
- **Error Handling**: Better exception handling

## 🚀 **How It Works**

### 1. **Application Startup**
```
Spring Boot starts
DatabaseConfig.initDatabase() runs
CREATE TABLE IF NOT EXISTS tickets
Table structure verification
data.sql executes (if present)
Sample data inserted
```

### 2. **Ticket Operations**
```
Create Ticket → createdAt auto-set
Update Ticket → updatedAt auto-updated
Query Tickets → Fast with indexes
```

## 📋 **Table Structure**

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT AUTO_INCREMENT | Primary key |
| `subject` | VARCHAR(255) | Ticket title |
| `description` | LONGTEXT | Detailed description |
| `user_id` | INT | Creator user ID |
| `assigned_to` | INT | Assignee user ID |
| `status` | VARCHAR(50) | Current status |
| `tag` | VARCHAR(100) | Ticket category |
| `created_at` | TIMESTAMP | Auto creation time |
| `updated_at` | TIMESTAMP | Auto update time |

## 🔍 **Verification**

### **Check Table Creation**
```sql
DESCRIBE tickets;
SHOW CREATE TABLE tickets;
```

### **View Sample Data**
```sql
SELECT * FROM tickets;
SELECT * FROM option_schema WHERE schema_name LIKE 'ticket%';
```

### **Check Indexes**
```sql
SHOW INDEX FROM tickets;
```

## 🧪 **Testing**

Run the included test:
```bash
mvn test -Dtest=DatabaseConfigTest
```

## 🐛 **Troubleshooting**

### **Table Not Created**
1. Check MySQL connection
2. Verify database exists
3. Check application logs for errors
4. Ensure proper MySQL permissions

### **Column Type Issues**
- The entity now uses `LONGTEXT` instead of `TEXT`
- Should work with both MySQL 5.7+ and MySQL 8+
- No manual table alterations needed

### **Timestamp Issues**
- Timestamps are handled automatically by Hibernate
- `@CreationTimestamp` and `@UpdateTimestamp` annotations
- No manual date setting required

## 📝 **Sample Usage**

### **Create Ticket**
```java
Ticket ticket = new Ticket();
ticket.setSubject("Bug Report");
ticket.setDescription("Found a critical bug");
ticket.setUserId(1);
ticket.setStatus("PENDING");
ticketRepository.save(ticket); // Timestamps set automatically
```

### **Update Ticket**
```java
TicketDto dto = new TicketDto();
dto.setId(1);
dto.setStatus("APPROVED");
ticketService.updateTicket(dto); // updatedAt set automatically
```

## ✅ **Success Indicators**

When everything works correctly, you should see:
```
✅ Tickets table created or verified successfully
📋 Tickets table structure:
id - int - NO
subject - varchar(255) - NO
description - longtext - YES
user_id - int - YES
assigned_to - int - YES
status - varchar(50) - YES
tag - varchar(100) - YES
created_at - timestamp - NO
updated_at - timestamp - NO
```

The ticket system is now fully configured for automatic table creation and management! 🎉
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package EmployeeSystem.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class DatabaseConfig {

@Autowired
private DataSource dataSource;

@Bean
public CommandLineRunner initDatabase() {
return args -> {
try (Connection connection = dataSource.getConnection()) {
// Ensure the tickets table exists with proper structure
String createTicketsTable = "CREATE TABLE IF NOT EXISTS tickets (" +
"id INT AUTO_INCREMENT PRIMARY KEY," +
"subject VARCHAR(255) NOT NULL," +
"description LONGTEXT," +
"user_id INT," +
"assigned_to INT," +
Comment on lines +29 to +30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The SQL schema defines user_id and assigned_to as INT columns. It would be beneficial to add foreign key constraints referencing the users table (assuming one exists) to enforce referential integrity at the database level. This helps prevent orphaned tickets if users are deleted.

"status VARCHAR(50) DEFAULT 'PENDING'," +
"tag VARCHAR(100)," +
"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," +
"updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
"INDEX idx_user_id (user_id)," +
"INDEX idx_assigned_to (assigned_to)," +
"INDEX idx_status (status)," +
"INDEX idx_created_at (created_at)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
Comment on lines +25 to +39

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a CommandLineRunner with raw SQL for database schema management can be less maintainable than using dedicated tools like Flyway or Liquibase, or relying solely on Hibernate's ddl-auto feature combined with entity definitions. While CREATE TABLE IF NOT EXISTS makes it idempotent, managing schema changes over time (e.g., adding columns, altering types) becomes more complex with this approach compared to migration scripts.


try (Statement statement = connection.createStatement()) {
statement.execute(createTicketsTable);
log.info("✅ Tickets table created or verified successfully");

// Verify table structure
java.sql.ResultSet resultSet = statement.executeQuery("DESCRIBE tickets");
log.info("📋 Tickets table structure:");
while (resultSet.next()) {
log.info(" {} - {} - {}",
resultSet.getString("Field"),
resultSet.getString("Type"),
resultSet.getString("Null"));
}
}

} catch (Exception e) {
log.error("❌ Failed to initialize tickets table: {}", e.getMessage());
throw new RuntimeException("Database initialization failed", e);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.*;
import java.time.LocalDateTime;


@Entity
Expand All @@ -27,19 +30,11 @@ public class Ticket {
* Updated the @Column annotation for description to use columnDefinition = "TEXT".
* This tells Hibernate to create the column as a TEXT type in the database,
* which can hold much larger strings compared to a VARCHAR.
*
*
* NOTE !!!
* if app failed to start, comment out "@Column(name = "description", columnDefinition = "TEXT")"
* start app, then modify table attr type manually via below cmd :
*
* ALTER TABLE tickets MODIFY COLUMN description TEXT;
*
* -> then run app again
*
* Fixed to work with both MySQL 5 and MySQL 8 for auto table creation.
*/
Comment on lines +34 to 35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment mentions fixing compatibility with MySQL 5 and 8. While LONGTEXT is generally supported, relying on columnDefinition can sometimes lead to portability issues across different database systems or versions. Ensure this definition works correctly with your target database environment(s).

//@Column(name = "description")
@Column(name = "description", columnDefinition = "TEXT")
private String description; // Use TEXT type for longer strings
@Column(name = "description", columnDefinition = "LONGTEXT")
private String description; // Use LONGTEXT type for longer strings

@Column(name = "user_id")
private Integer userId;
Expand All @@ -52,4 +47,12 @@ public class Ticket {

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

@CreationTimestamp
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;

@UpdateTimestamp
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
Expand All @@ -18,4 +20,6 @@ public class TicketDto {
private Integer assignedTo; // assigned user id
private String status;
private String tag;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,21 @@ public Ticket getTicketById(Integer ticketId) {
}

public void updateTicket(TicketDto ticketDto) {

Ticket ticket = new Ticket();
ticketRepository.deleteById(ticketDto.getId());
BeanUtils.copyProperties(ticketDto, ticket);
ticketRepository.save(ticket);

// Find existing ticket to preserve creation time and ID
Ticket existingTicket = ticketRepository.findById(ticketDto.getId())
.orElseThrow(() -> new RuntimeException("Ticket not found with id: " + ticketDto.getId()));

// Update only the fields that should be modified
existingTicket.setSubject(ticketDto.getSubject());
existingTicket.setDescription(ticketDto.getDescription());
existingTicket.setUserId(ticketDto.getUserId());
existingTicket.setAssignedTo(ticketDto.getAssignedTo());
existingTicket.setStatus(ticketDto.getStatus());
existingTicket.setTag(ticketDto.getTag());

// Save the updated ticket (updatedAt will be set automatically)
ticketRepository.save(existingTicket);
}

public void addTicket(Ticket ticket) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update

spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.properties.hibernate.hbm2ddl.auto=update

server.port=9998

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- This file will be executed by Spring Boot on startup to initialize data
-- It creates sample data for testing the ticket system

-- Insert sample ticket statuses into option_schema if they don't exist
INSERT IGNORE INTO option_schema (column_name, schema_name, active) VALUES
('PENDING', 'ticket_status', true),
('CREATED', 'ticket_status', true),
('REVIEWING', 'ticket_status', true),
('APPROVED', 'ticket_status', true),
('REJECTED', 'ticket_status', true),
('CANCELLED', 'ticket_status', true);

-- Insert sample ticket tags into option_schema if they don't exist
INSERT IGNORE INTO option_schema (column_name, schema_name, active) VALUES
('Bug Report', 'ticket_tag', true),
('Feature Request', 'ticket_tag', true),
('Technical Support', 'ticket_tag', true),
('HR Request', 'ticket_tag', true),
('IT Support', 'ticket_tag', true),
('General Inquiry', 'ticket_tag', true);

-- Create sample tickets if tickets table is empty
INSERT IGNORE INTO tickets (id, subject, description, user_id, assigned_to, status, tag, created_at, updated_at) VALUES
(1, 'Sample IT Support Ticket', 'This is a sample ticket for IT support request to test the system functionality.', 1, 2, 'PENDING', 'IT Support', NOW(), NOW()),
(2, 'Feature Request Example', 'Sample feature request ticket to demonstrate the ticketing system capabilities.', 2, 1, 'REVIEWING', 'Feature Request', NOW(), NOW());
Loading
Loading