{"id":44005278,"url":"https://github.com/closeup1202/spring-transaction-inspector-plugin","last_synced_at":"2026-04-03T02:03:18.399Z","repository":{"id":319969942,"uuid":"1080212272","full_name":"closeup1202/spring-transaction-inspector-plugin","owner":"closeup1202","description":"Catch Spring @Transactional pitfalls before they reach production. This plugin performs static analysis to detect common transaction anti-patterns and configuration issues.","archived":false,"fork":false,"pushed_at":"2026-02-03T15:46:03.000Z","size":196,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-07T20:14:58.345Z","etag":null,"topics":["code","debugging","inspector","jpa","spring","spring-boot","springboot","transactional"],"latest_commit_sha":null,"homepage":"https://plugins.jetbrains.com/plugin/28789-spring-transaction-inspector","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/closeup1202.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-21T03:14:38.000Z","updated_at":"2026-02-07T01:45:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/closeup1202/spring-transaction-inspector-plugin","commit_stats":null,"previous_names":["closeup1202/spring-transaction-inspector-plugin"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/closeup1202/spring-transaction-inspector-plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/closeup1202%2Fspring-transaction-inspector-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/closeup1202%2Fspring-transaction-inspector-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/closeup1202%2Fspring-transaction-inspector-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/closeup1202%2Fspring-transaction-inspector-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/closeup1202","download_url":"https://codeload.github.com/closeup1202/spring-transaction-inspector-plugin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/closeup1202%2Fspring-transaction-inspector-plugin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31326879,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T01:42:14.489Z","status":"online","status_checked_at":"2026-04-03T02:00:06.642Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code","debugging","inspector","jpa","spring","spring-boot","springboot","transactional"],"created_at":"2026-02-07T13:11:26.336Z","updated_at":"2026-04-03T02:03:18.393Z","avatar_url":"https://github.com/closeup1202.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring Transaction Inspector\n\n[![JetBrains Plugin](https://img.shields.io/badge/JetBrains-Plugin-blue.svg)](https://plugins.jetbrains.com/plugin/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nIntelliJ IDEA plugin for detecting common Spring `@Transactional` pitfalls and anti-patterns through static code analysis.\n\n## 🎯 Why This Plugin?\n\nSpring's `@Transactional` is powerful but has many gotchas that even experienced developers miss:\n- Methods that silently ignore transaction settings due to AOP proxy bypass\n- N+1 query performance issues that only show up in production\n- Checked exceptions that don't trigger rollback, causing data inconsistency\n- Transaction propagation conflicts that cause runtime errors\n\nThis plugin catches these issues **while you code**, before they reach production.\n\n## ✨ Features\n\n### 🔍 8 Comprehensive Inspections\n\n#### 1. **AOP Proxy Bypass Detection**\nDetects when `@Transactional` methods are called within the same class, causing transaction settings to be ignored.\n\n```java\n@Service\npublic class UserService {\n    @Transactional\n    public void createUser() {\n        updateUser();  // ⚠️ AOP proxy bypassed - transaction settings ignored!\n    }\n\n    @Transactional(propagation = Propagation.REQUIRES_NEW)\n    public void updateUser() {\n        // Won't run in a new transaction!\n    }\n}\n```\n\n#### 2. **Invalid Method Modifiers**\nPrevents `@Transactional` on private/final/static methods where Spring AOP cannot intercept.\n\n```java\n@Service\npublic class OrderService {\n    @Transactional\n    private void processOrder() {  // ❌ Spring AOP cannot intercept private methods!\n        orderRepository.save(order);\n    }\n}\n```\n\n#### 3. **Transaction Propagation Conflict Detection** 🆕\nDetects MANDATORY/NEVER/REQUIRES_NEW conflicts that cause runtime exceptions or data inconsistency.\n\n**MANDATORY - Called without transaction:**\n```java\npublic void updateInventory() {  // No @Transactional\n    decreaseStock(productId, 10);  // ❌ ERROR!\n}\n\n@Transactional(propagation = Propagation.MANDATORY)\npublic void decreaseStock(Long productId, int quantity) {\n    // Throws IllegalTransactionStateException at runtime!\n}\n```\n\n**NEVER - Called within transaction:**\n```java\n@Transactional\npublic void registerUser(User user) {\n    emailService.sendEmail(user);  // ❌ ERROR!\n}\n\n@Transactional(propagation = Propagation.NEVER)\npublic void sendEmail(User user) {\n    // Throws IllegalTransactionStateException at runtime!\n}\n```\n\n**REQUIRES_NEW - Data inconsistency risk:**\n```java\n@Transactional\npublic void createOrder(Order order) {\n    orderRepository.save(order);           // Transaction 1\n    paymentService.processPayment(order);  // ⚠️ Transaction 2 (independent!)\n    // If exception here, order rolls back but payment is committed!\n}\n\n@Transactional(propagation = Propagation.REQUIRES_NEW)\npublic void processPayment(Order order) { }\n```\n\n#### 4. **N+1 Query Detection** (Enhanced!)\nIdentifies lazy-loaded relationships accessed in loops/streams that cause performance issues.\n\nNow detects **all JPA relationship types:**\n- `@OneToMany` (LAZY by default)\n- `@ManyToMany` (LAZY by default)\n- `@ManyToOne(fetch = LAZY)` 🆕\n- `@OneToOne(fetch = LAZY)` 🆕\n\n```java\n@Transactional\npublic void printPosts() {\n    List\u003cPost\u003e posts = postRepository.findAll();  // 1 query\n\n    for (Post post : posts) {\n        post.getUser().getName();  // ⚠️ N queries! (1 per post)\n    }\n}\n```\n\n**Solution:**\n```java\n@Query(\"SELECT p FROM Post p JOIN FETCH p.user\")\nList\u003cPost\u003e findAllWithUser();  // ✅ Single query with JOIN\n```\n\n#### 5. **ReadOnly Transaction Write Operations**\nDetects write operations (save/update/delete) in `@Transactional(readOnly=true)` methods.\n\n```java\n@Transactional(readOnly = true)\npublic void processData() {\n    User user = userRepository.findById(1L);\n    user.setName(\"Updated\");\n    userRepository.save(user);  // ⚠️ Write operation in readOnly transaction!\n}\n```\n\n#### 6. **Checked Exception Rollback**\nWarns when methods throw checked exceptions without `rollbackFor` configuration.\n\n```java\n@Transactional\npublic void processFile() throws IOException {\n    orderRepository.save(order);        // DB write\n    fileService.uploadFile(file);       // IOException thrown\n    // ❌ Transaction commits despite exception! Data inconsistency!\n}\n```\n\n**Solution:**\n```java\n@Transactional(rollbackFor = Exception.class)  // ✅ Rollback on all exceptions\npublic void processFile() throws IOException {\n    // ...\n}\n```\n\n#### 7. **@Async and @Transactional Conflicts**\nDetects three critical async-transaction patterns:\n\n**Pattern 1: @Async + @Transactional on same method**\n```java\n@Async\n@Transactional  // ❌ Transaction doesn't propagate to async thread!\npublic void processAsync(User user) {\n    userRepository.save(user);  // No transaction context!\n}\n```\n\n**Pattern 2: Lazy loading in @Async methods**\n```java\n@Async\npublic void processUserPosts(User user) {\n    int count = user.getPosts().size();  // ❌ LazyInitializationException!\n}\n```\n\n**Pattern 3: Same-class @Async calls**\n```java\n@Service\npublic class UserService {\n    public void createUser() {\n        processAsync();  // ❌ Executes synchronously (AOP bypass)!\n    }\n\n    @Async\n    private void processAsync() { }\n}\n```\n\n#### 8. **ReadOnly Transaction Calling Write Methods**\nDetects when `@Transactional(readOnly=true)` methods call write-capable methods, causing runtime errors.\n\n```java\n@Service\npublic class UserService {\n    @Transactional(readOnly = true)\n    public void viewUserData() {\n        User user = userRepository.findById(1L);\n        updateUserStats();  // 🔴 ERROR: Two problems!\n    }\n\n    @Transactional  // REQUIRED is default\n    public void updateUserStats() {\n        statsRepository.save(new Stats());\n        // ❌ Runtime error: \"Write operations not allowed in read-only mode\"\n    }\n}\n```\n\n**Smart Detection:**\n- **Same-class call**: Shows ERROR (not just warning) because `REQUIRES_NEW` won't work due to AOP bypass\n- **Different-class call**: Shows WARNING with Quick Fix to change propagation to `REQUIRES_NEW`\n\n**Solutions:**\n\n✅ **Option 1: Extract to separate service**\n```java\n@Service\npublic class UserService {\n    @Autowired\n    private UserStatsService statsService;\n\n    @Transactional(readOnly = true)\n    public void viewUserData() {\n        statsService.updateUserStats();  // ✅ OK\n    }\n}\n\n@Service\npublic class UserStatsService {\n    @Transactional(propagation = Propagation.REQUIRES_NEW)\n    public void updateUserStats() { }\n}\n```\n\n✅ **Option 2: Remove readOnly if writes needed**\n```java\n@Transactional  // readOnly=false by default\npublic void viewUserData() {\n    User user = userRepository.findById(1L);\n    updateUserStats();  // ✅ Both in write-capable transaction\n}\n```\n\n### 🎨 Visual Indicators\n- **Gutter icons** for `@Transactional` methods\n- **Different icons** for read-only vs write transactions\n- **Hover tooltips** with transaction configuration details\n\n### ⚡ Quick Fixes\n- Add `rollbackFor = Exception.class`\n- Add specific exception types to `rollbackFor`\n- Change method visibility (private → public/protected)\n- Remove invalid modifiers (final/static)\n- Change propagation to `REQUIRES_NEW`\n- Remove conflicting annotations\n\n### ⚙️ Customization\nFine-grained control over which inspections to enable:\n\n**Settings → Tools → Spring Transaction Inspector**\n- ✓ Detect same-class @Transactional method calls\n- ✓ Warn on private methods with @Transactional\n- ✓ Warn on final methods with @Transactional\n- ✓ Warn on static methods with @Transactional\n- ✓ Warn on checked exceptions without rollbackFor\n- ✓ Detect @Async and @Transactional conflicts\n- ✓ Detect write method calls from readOnly transactions\n- ✓ Detect transaction propagation conflicts (MANDATORY/NEVER/REQUIRES_NEW)\n- ✓ Enable N+1 query detection\n  - ✓ Check in stream operations (.map, .flatMap)\n  - ✓ Check in for-each loops\n- ✓ Show gutter icons for @Transactional methods\n  - ✓ Show different icon for readOnly transactions\n\n## 📦 Installation\n\n### From JetBrains Marketplace (Recommended)\n1. Open IntelliJ IDEA\n2. Go to `Settings/Preferences → Plugins → Marketplace`\n3. Search for **\"Spring Transaction Inspector\"**\n4. Click `Install`\n5. Restart IDE\n\n### Manual Installation\n1. Download the latest release from [Releases](https://github.com/closeup1202/spring-transaction-inspector-plugin/releases)\n2. Go to `Settings/Preferences → Plugins → ⚙️ → Install Plugin from Disk`\n3. Select the downloaded `.zip` file\n4. Restart IDE\n\n## 🚀 Usage\n\n### Automatic Detection\nOnce installed, the plugin automatically analyzes your code:\n- **Real-time warnings** appear as you type\n- **Gutter icons** show transaction methods\n- **Quick fixes** available via `Alt + Enter` (or `⌥ + ⏎` on Mac)\n\n### Manual Inspection\nRight-click on a method → `Show Transaction Info`\n\n### Configuration\n`Settings/Preferences → Tools → Spring Transaction Inspector`\n\n## 💡 Best Practices Enforced\n\n### ✅ DO\n```java\n// Separate concerns - different services\n@Service\npublic class OrderService {\n    @Autowired\n    private NotificationService notificationService;\n\n    @Transactional\n    public void createOrder(Order order) {\n        orderRepository.save(order);\n        notificationService.sendEmail(order);  // ✅ Different class\n    }\n}\n\n// Use @EntityGraph or JOIN FETCH for N+1\n@Query(\"SELECT u FROM User u LEFT JOIN FETCH u.posts\")\nList\u003cUser\u003e findAllWithPosts();\n\n// Specify rollbackFor for checked exceptions\n@Transactional(rollbackFor = Exception.class)\npublic void processFile() throws IOException { }\n```\n\n### ❌ DON'T\n```java\n// Same-class transactional call\n@Service\npublic class OrderService {\n    @Transactional\n    public void createOrder() {\n        notifyCustomer();  // ❌ Same class - AOP bypassed\n    }\n\n    @Transactional(propagation = Propagation.REQUIRES_NEW)\n    private void notifyCustomer() { }\n}\n\n// Lazy loading in loops\nList\u003cUser\u003e users = userRepository.findAll();\nfor (User user : users) {\n    user.getPosts().size();  // ❌ N+1 queries\n}\n\n// @Transactional on private method\n@Transactional\nprivate void saveOrder() { }  // ❌ AOP cannot intercept\n```\n\n## 📊 Supported Frameworks\n\n- **Spring Framework** 5.x, 6.x\n- **Spring Boot** 2.x, 3.x\n- **JPA/Hibernate** (for N+1 detection)\n- **Jakarta Persistence** (JPA 3.0+)\n\n## 🔧 Requirements\n\n- **IntelliJ IDEA** 2024.2+ (Community or Ultimate Edition)\n- **Java** 21+\n- **Kotlin** 2.1.0+ (for plugin development)\n\n## 🤝 Contributing\n\nContributions are welcome! Here's how you can help:\n\n1. **Report bugs**: [Open an issue](https://github.com/closeup1202/spring-transaction-inspector-plugin/issues)\n2. **Suggest features**: [Start a discussion](https://github.com/closeup1202/spring-transaction-inspector-plugin/discussions)\n3. **Submit PRs**: Fork, code, test, and submit!\n\n### Development Setup\n```bash\ngit clone https://github.com/closeup1202/spring-transaction-inspector-plugin.git\ncd spring-transaction-inspector-plugin\n./gradlew runIde  # Launch IDE with plugin\n./gradlew test    # Run tests TODO: Some tests are currently failing; will fix later\n```\n\n## 📝 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 👤 Author\n\n**[@closeup1202](https://github.com/closeup1202)**\n\n## 🙏 Acknowledgments\n\n- Inspired by real-world Spring transaction issues encountered in production\n- Built with [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html)\n\n## 📮 Support\n\n- **Issues**: [GitHub Issues](https://github.com/closeup1202/spring-transaction-inspector-plugin/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/closeup1202/spring-transaction-inspector-plugin/discussions)\n\n---\n\n**If this plugin helped you catch a bug, please ⭐ star the repo!**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloseup1202%2Fspring-transaction-inspector-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloseup1202%2Fspring-transaction-inspector-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloseup1202%2Fspring-transaction-inspector-plugin/lists"}