Skip to content

sshashankshekhar30/Custom-AI-app-development-using-Java-spring-boot-MySQL-MongoDB-

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

Custom-AI-app-development-using-Java-spring-boot-MySQL-MongoDB-

Custom AI app development using Java, spring boot, MySQL, MongoDB

Nice — I’ll give you a complete, working example you can run locally or deploy to any cloud (AWS, GCP, Railway, Render, Fly.io). The example is a small, practical AI-backed Notes app that shows how to:

  • store user notes in MySQL (relational data),
  • create & store text embeddings in MongoDB (vector store),
  • use an LLM / embedding API to generate embeddings (via OpenAI-compatible REST call), and
  • do semantic search (cosine similarity) across stored notes.

This stack uses Java + Spring Boot (REST API), Spring Data JPA for MySQL, Spring Data MongoDB for embeddings, and plain HTTP calls to the embeddings API. It’s low-complexity, production-capable, and easy to extend to more advanced vector search later (Milvus, Pinecone, or MongoDB Atlas Vector Search).


Overview — what you’ll get

  1. docker-compose.yml to run MySQL + MongoDB locally.

  2. pom.xml (Spring Boot project) and application.yml for configuration.

  3. Java classes:

    • Note (JPA entity stored in MySQL)
    • Embedding (MongoDB document storing vector + noteId)
    • NoteRepository (Spring Data JPA)
    • EmbeddingRepository (Spring Data MongoDB)
    • EmbeddingService (requests embeddings from OpenAI-like API)
    • NoteService (business logic: save note + create embedding)
    • NoteController (REST endpoints for create note, search)
  4. How to run and sample curl commands.

  5. Notes on deploying to cloud and production improvements.

Requirements to run: Java 17+, Maven, Docker (for local DBs), an OpenAI-compatible embedding API key (set as OPENAI_API_KEY environment variable). You can substitute any embedding provider with a compatible REST endpoint.


1) docker-compose.yml — MySQL + MongoDB

Create docker-compose.yml:

version: "3.8"
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: ai_notes
      MYSQL_USER: aiuser
      MYSQL_PASSWORD: aipass
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 5s
      retries: 12

  mongo:
    image: mongo:6.0
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db

volumes:
  mongo_data:

Run:

docker compose up -d

2) pom.xml (key dependencies)

Create a Spring Boot app pom.xml:

<project ...>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>ai-notes</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
    <java.version>17</java.version>
    <spring.boot.version>3.1.6</spring.boot.version>
  </properties>

  <dependencies>
    <!-- Spring Boot -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- JPA / MySQL -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
    </dependency>

    <!-- MongoDB -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>

    <!-- JSON parsing -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- Lombok (optional, reduces boilerplate) -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

    <!-- Dev / test -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

3) application.yml — app config

src/main/resources/application.yml:

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ai_notes?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
    username: aiuser
    password: aipass
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: false

  data:
    mongodb:
      host: localhost
      port: 27017
      database: ai_notes_vectors

app:
  openai:
    api-url: https://api.openai.com/v1/embeddings
    model: text-embedding-3-small

In production, put DB credentials and API keys in environment variables or your cloud secret manager.


4) Java code — main pieces

Create package com.example.aionotes.

AiNotesApplication.java

package com.example.aionotes;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Note (JPA entity stored in MySQL)

package com.example.aionotes.model;

import jakarta.persistence.*;
import java.time.Instant;

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

    private String title;

    @Column(columnDefinition = "TEXT")
    private String content;

    private Instant createdAt = Instant.now();

    // getters and setters (or use Lombok @Data)
    // ...
}

Embedding (MongoDB document)

package com.example.aionotes.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Document(collection = "embeddings")
public class Embedding {
    @Id
    private String id;
    private Long noteId;
    private List<Double> vector;

    // getters and setters
    // ...
}

NoteRepository (JPA)

package com.example.aionotes.repository;

import com.example.aionotes.model.Note;
import org.springframework.data.jpa.repository.JpaRepository;

public interface NoteRepository extends JpaRepository<Note, Long> {}

EmbeddingRepository (Mongo)

package com.example.aionotes.repository;

import com.example.aionotes.model.Embedding;
import org.springframework.data.mongodb.repository.MongoRepository;

import java.util.List;

public interface EmbeddingRepository extends MongoRepository<Embedding, String> {
    List<Embedding> findAll();
    Embedding findByNoteId(Long noteId);
}

OpenAIEmbeddingClient (call embeddings API)

A simple RestTemplate-based client to call the embeddings endpoint. Use your provider endpoint if different.

package com.example.aionotes.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class OpenAIEmbeddingClient {

    @Value("${app.openai.api-url}")
    private String apiUrl;

    @Value("${app.openai.model}")
    private String model;

    private final String apiKey = System.getenv("OPENAI_API_KEY"); // must be set

    private final RestTemplate rest = new RestTemplate();
    private final ObjectMapper mapper = new ObjectMapper();

    public List<Double> getEmbedding(String text) throws Exception {
        if (apiKey == null || apiKey.isEmpty()) {
            throw new IllegalStateException("OPENAI_API_KEY environment variable not set");
        }

        Map<String, Object> payload = new HashMap<>();
        payload.put("model", model);
        payload.put("input", text);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(apiKey);

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);
        ResponseEntity<String> response = rest.postForEntity(apiUrl, request, String.class);

        if (!response.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("Embedding API responded with " + response.getStatusCode());
        }

        JsonNode root = mapper.readTree(response.getBody());
        // path depends on provider; OpenAI returns data[0].embedding
        JsonNode embeddingNode = root.path("data").get(0).path("embedding");
        List<Double> vector = new ArrayList<>();
        for (JsonNode n : embeddingNode) {
            vector.add(n.asDouble());
        }
        return vector;
    }
}

NoteService

package com.example.aionotes.service;

import com.example.aionotes.model.Embedding;
import com.example.aionotes.model.Note;
import com.example.aionotes.repository.EmbeddingRepository;
import com.example.aionotes.repository.NoteRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class NoteService {

    private final NoteRepository noteRepository;
    private final EmbeddingRepository embeddingRepository;
    private final OpenAIEmbeddingClient embeddingClient;

    public NoteService(NoteRepository noteRepository,
                       EmbeddingRepository embeddingRepository,
                       OpenAIEmbeddingClient embeddingClient) {
        this.noteRepository = noteRepository;
        this.embeddingRepository = embeddingRepository;
        this.embeddingClient = embeddingClient;
    }

    public Note createNote(String title, String content) throws Exception {
        Note note = new Note();
        note.setTitle(title);
        note.setContent(content);
        Note saved = noteRepository.save(note);

        // create embedding
        List<Double> vector = embeddingClient.getEmbedding(content);
        Embedding emb = new Embedding();
        emb.setNoteId(saved.getId());
        emb.setVector(vector);
        embeddingRepository.save(emb);

        return saved;
    }

    public List<Embedding> allEmbeddings() {
        return embeddingRepository.findAll();
    }

    // cosine similarity util
    public static double cosineSim(List<Double> a, List<Double> b) {
        double dot = 0.0, normA = 0.0, normB = 0.0;
        for (int i = 0; i < a.size(); i++) {
            dot += a.get(i) * b.get(i);
            normA += Math.pow(a.get(i), 2);
            normB += Math.pow(b.get(i), 2);
        }
        if (normA == 0 || normB == 0) return 0.0;
        return dot / (Math.sqrt(normA) * Math.sqrt(normB));
    }
}

SearchResult DTO

package com.example.aionotes.dto;

import com.example.aionotes.model.Note;

public class SearchResult {
    private Note note;
    private double score;
    // constructor, getters/setters
}

NoteController — endpoints: create note, search

package com.example.aionotes.controller;

import com.example.aionotes.dto.SearchResult;
import com.example.aionotes.model.Embedding;
import com.example.aionotes.model.Note;
import com.example.aionotes.repository.NoteRepository;
import com.example.aionotes.service.NoteService;
import com.example.aionotes.service.OpenAIEmbeddingClient;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

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

    private final NoteService noteService;
    private final NoteRepository noteRepository;
    private final OpenAIEmbeddingClient embeddingClient;

    public NoteController(NoteService noteService,
                          NoteRepository noteRepository,
                          OpenAIEmbeddingClient embeddingClient) {
        this.noteService = noteService;
        this.noteRepository = noteRepository;
        this.embeddingClient = embeddingClient;
    }

    @PostMapping("/notes")
    public Note createNote(@RequestBody Map<String,String> body) throws Exception {
        String title = body.getOrDefault("title", "Untitled");
        String content = body.getOrDefault("content", "");
        return noteService.createNote(title, content);
    }

    @GetMapping("/notes")
    public List<Note> listNotes() { return noteRepository.findAll(); }

    @GetMapping("/search")
    public List<SearchResult> search(@RequestParam("q") String query,
                                     @RequestParam(value="top", defaultValue="5") int top) throws Exception {
        List<Double> qVec = embeddingClient.getEmbedding(query);
        List<Embedding> all = noteService.allEmbeddings();

        // compute similarity
        List<SearchResult> results = new ArrayList<>();
        for (Embedding emb : all) {
            double score = NoteService.cosineSim(qVec, emb.getVector());
            Optional<Note> noteOpt = noteRepository.findById(emb.getNoteId());
            noteOpt.ifPresent(n -> {
                SearchResult r = new SearchResult();
                r.setNote(n);
                r.setScore(score);
                results.add(r);
            });
        }
        return results.stream()
                .sorted(Comparator.comparingDouble(SearchResult::getScore).reversed())
                .limit(top)
                .collect(Collectors.toList());
    }
}

Note: Add standard getters/setters or use Lombok (@Data) to reduce boilerplate.


5) Run the app locally

  1. Start DBs:
docker compose up -d
  1. Build & run Spring Boot:
export OPENAI_API_KEY="sk-..."   # your key
mvn clean spring-boot:run

If using an alternative provider, set app.openai.api-url and app.openai.model accordingly.


6) Example API calls (curl)

Create a note:

curl -X POST http://localhost:8080/api/notes \
 -H "Content-Type: application/json" \
 -d '{"title":"Phishing report","content":"Suspicious email asked for password reset and link pointed to bad.example.com"}'

Search semantically:

curl "http://localhost:8080/api/search?q=how+to+report+phishing&top=5"

You’ll get notes with score (cosine similarity) — highest scores are most semantically relevant.


7) Production & Cloud notes (free / low cost options)

  • Databases: Use managed MySQL / Amazon RDS free-tier or Google Cloud Cloud SQL free credits; MongoDB Atlas has a free tier with vector search features.
  • Hosting: Railway, Render, Fly.io, or a small EC2/GCE instance. All have free trial credits.
  • Vector DB upgrade: For scale, move vector storage to Milvus, Pinecone, or MongoDB Atlas Vector Search (supports ANN).
  • Secrets: Use environment variables or cloud secret manager (AWS Secrets Manager, GCP Secret Manager).
  • Scaling: Move embedding calls to asynchronous jobs or background worker (e.g., Spring @Async or a message queue) if you have high volume.

8) Improvements & extensions (next steps)

  • Use ANN index (HNSW) for large-scale fast vector search (Pinecone, Milvus, or Elastic kNN).
  • Add user accounts and per-user security (JWT, OAuth).
  • Cache embeddings for repeated queries.
  • Add rate-limiting, request quotas and billing (Stripe integration).
  • Add automatic chunking for long documents and store chunk embeddings for better recall.
  • Add monitoring, logging, and observability (Prometheus + Grafana, ELK).

9) Security & compliance notes

  • Don’t send PII to public LLMs unless you have consent and your provider's TOS supports that.
  • Use encryption at rest (DB-level) and TLS for network traffic.
  • Protect API keys; never hardcode them.

Final pointers — how to use this for business

  • Wrap this backend with a polished frontend (React/Vue or a no-code frontend like Bubble for quicker demos).
  • Offer a hosted SaaS (multi-tenant) or white-label the service for customers.
  • Add subscription & billing + onboarding flows for monetization.
  • Use domain expertise (e.g., cybersecurity) to build vertical-specific prompts and templates — that’s your product moat.

If you want, I can now:

  • generate the full repository structure and zippable project files, or
  • produce a Dockerfile + docker-compose that runs the Spring Boot app with MySQL and Mongo together, or
  • convert the embedding storage to use a MongoDB Atlas Vector Index and show exact config steps.

Which one should I create for you immediately?

About

Custom AI app development using Java, spring boot, MySQL, MongoDB

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published