I have code based on DAO architecture with Maven on IDEA Ultimate 2024.2.2 In it, I use Spring + Hibernate to work with the MySQL database and Project Lombok to work with User entity annotations. It would seem that I set everything up according to the instructions on the Internet, but my Spring doesn’t want to start. I'm trying to execute the following code:
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext("applicationContext.xml");
UserDaoHibernateImpl userService = context.getBean(UserDaoHibernateImpl.class);
userService.createUsersTable();
}
}
but this code gives me the following exception: Console:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'jm.task.core.jdbc.dao.UserDaoHibernateImpl' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1261)
at jm.task.core.jdbc.Application.main(Application.java:17)
and even if I try to execute this code from my class for tests, I get the same exception.
My project tree:
/kata_preproj_with_lombok
│ pom.xml
├── /src
│ ├── /main
│ │ ├── /java
│ │ │ ├── /jm.task.core.jdbc
│ │ │ │ ├── Application.class // this is the Main class I'm trying to run
│ │ │ │ ├── /dao
│ │ │ │ │ ├── Userdao.java
│ │ │ │ │ ├── UserDaoHibernateImpl.java
│ │ │ │ ├── /model
│ │ │ │ │ ├── User.class // entity
│ │ │ │ ├── /service // dao business logic
│ │ │ │ │ ├── UserService.class
│ │ │ │ │ ├── UserServiceImpl.class // contains UserDaoHibernateImpl methods
│ │ │ │ ├── Util // connection directory
│ │ │ │ │ ├── Util.class
│ │ ├── /resources
│ │ │ ├── database.properties // properties for connecting to the MySQL database (task requirement)
│ │ │ ├── log4j.properties // logger properties
│ │ │ ├── applicationContext.xml // Spring config with beans
├── /test
│ ├── /java
│ │ ├── UserServiceTest.java // contains all tests for project
My UserDaoHibernateImpl:
@Repository
@Transactional
public class UserDaoHibernateImpl implements UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDaoHibernateImpl.class);
private final SessionFactory sessionFactory;
@Autowired
public UserDaoHibernateImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
@Transactional
public void createUsersTable() {
logger.debug("Creating users table");
try {
Session session = sessionFactory.getCurrentSession();
NativeQuery<?> query = session.createNativeQuery("CREATE TABLE IF NOT EXISTS users (" +
"id BIGINT PRIMARY KEY AUTO_INCREMENT," +
"name VARCHAR(45)," +
"lastname VARCHAR(45)," +
"age TINYINT)");
query.executeUpdate();
logger.info("Users table created successfully.");
} catch (Exception e) {
logger.error("Error creating Users table.", e);
throw new RuntimeException(e);
}
}
@Override
@Transactional
public void dropUsersTable() {
logger.debug("Start dropping users table");
try {
Session session = sessionFactory.getCurrentSession();
NativeQuery<?> query = session.createNativeQuery("DROP TABLE IF EXISTS users");
query.executeUpdate();
logger.info("Users table dropped successfully.");
} catch (Exception e) {
logger.error("Error dropping Users table.", e);
throw new RuntimeException(e);
}
}
@Override
@Transactional
public void saveUser(String name, String lastName, byte age) {
logger.debug("Start saving User");
try {
Session session = sessionFactory.getCurrentSession();
User user = new User(null, name, lastName, age);
session.save(user);
logger.info("User saved: {}", user);
} catch (Exception e) {
logger.error("Error saving User.", e);
throw new RuntimeException(e);
}
}
@Override
@Transactional
public void removeUserById(long id) {
logger.debug("Start deleting User with id: {}", id);
try {
Session session = sessionFactory.getCurrentSession();
User user = session.get(User.class, id);
if (user != null) {
session.delete(user);
logger.info("User deleted with id: {}", id);
}
} catch (Exception e) {
logger.error("Error deleting User with id: {}", id, e);
throw new RuntimeException(e);
}
}
@Override
@Transactional(readOnly = true)
public List<User> getAllUsers() {
logger.debug("Start retrieving all Users");
try {
Session session = sessionFactory.getCurrentSession();
List<User> users = session.createQuery("from User", User.class).getResultList();
logger.info("All users retrieved.");
return users;
} catch (Exception e) {
logger.error("Error retrieving all users.", e);
throw new RuntimeException(e);
}
}
@Override
@Transactional
public void cleanUsersTable() {
logger.debug("Start cleaning Users table");
try {
Session session = sessionFactory.getCurrentSession();
NativeQuery<?> query = session.createNativeQuery("TRUNCATE TABLE users");
query.executeUpdate();
logger.info("Users table cleaned successfully.");
} catch (Exception e) {
logger.error("Error cleaning Users table.", e);
throw new RuntimeException(e);
}
}
}
My User entity with Project Lombok annotations:
@Data // equals and hashCode; getters and setters; and more
@NoArgsConstructor // constructor without args for Reflection API that uses in Hibernate
@AllArgsConstructor // constructor with all args from fields of class
@Builder // for creating and initializing class
@Entity // this class is entity of Hibernate
@Table(name = "users") // table name of database
public class User implements Serializable {
@Serial
private static final long serialVersionUID = 42L;
@Id // primary (first) key ---- ID type needs to be Serializable
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto increment ID value in table
@Column(name = "id") // column name in table (default is property name)
private Long id;
@Column(name = "name") // this name needs for great mapping
private String name;
@Column(name = "lastname")
private String lastName;
@Column(name = "age")
private Byte age;
}
My UserServiceImpl:
@Service
public class UserServiceImpl implements UserService {
private final UserDaoHibernateImpl userDao;
@Autowired
public UserServiceImpl(UserDaoHibernateImpl userDao) {
this.userDao = userDao;
}
@Override
@Transactional
public void createUsersTable() throws SQLException {
userDao.createUsersTable();
}
@Override
@Transactional
public void dropUsersTable() throws SQLException {
userDao.dropUsersTable();
}
@Override
@Transactional
public void saveUser(String name, String lastName, byte age) throws SQLException {
userDao.saveUser(name, lastName, age);
}
@Override
@Transactional
public void removeUserById(long id) throws SQLException {
userDao.removeUserById(id);
}
@Override
@Transactional
public List<User> getAllUsers() throws SQLException {
return userDao.getAllUsers();
}
@Override
@Transactional
public void cleanUsersTable() throws SQLException {
userDao.cleanUsersTable();
}
}
My connection class named Util:
public class Util {
static ResourceBundle bundle = ResourceBundle.getBundle("database"); // database.properties
// для JDBC присоединения
public static Connection getConnection() {
// переменные хранения данных от значений обращенных ссылок
String url = bundle.getString("mysql.url");
String username = bundle.getString("mysql.username");
String password = bundle.getString("mysql.password");
// реализация подключения
try {
return DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
My applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- scan for components (Services, Repositories, ...) -->
<context:component-scan base-package="jm.task.core.jdbc"/>
<!-- copy information from database.properties -->
<context:property-placeholder location="classpath:database.properties"/>
<!-- DataSource Bean Declaration -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${mysql.driverClassName}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</bean>
<!-- SessionFactory Bean Declaration -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="jm.task.core.jdbc.model"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- Transaction Manager Declaration -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
I tried to transfer annotations between interfaces and classes that initialize them, I added this line:
"SpringApplication.run(Application.class, args);"
but it didn't help.
Also in the following code:
UserDaoHibernateImpl userService = context.getBean(UserDaoHibernateImpl.class);
I tried to change the class used in the.getBean() argument to an interface, and also tried to change this class to UserService and its class, but it also did not help.
In the settings, IDEA set the Spring configuration to let IDEA know that it is a configuration
ChatGPT couldn't help me with this question :(
The problem is your main class (in multiple ways).
You have an XML file which needs to be loaded to parse the configuration for this you should use an XmlApplicationContext
not a annotation based one like the AnnotationConfigApplicationContext
. The AnnotationConfigApplicationContext
is for use with @Configuration
classes or component scanning directly.
What you should use is the ClassPathXmlApplicationContext
instead.
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean(UserService.class);
userService.createUsersTable();
}
}
In your main
you don't need SpringApplication.run
as that is for Spring Boot.
Now while this works the fact that you have SpringApplication.run
in there hints add that you are actually using Spring Boot (or at least the dependencies, not Spring Boot itself). So instead of what you should do with ClassPathXmlApplicationContext
you can utilize Spring Boot as well.
@SpringBootApplication
@ImportResouce("classpath:applicationContext.xml");
public class Application {
public static void main(String[] args) {
var context = SpringApplication.run(Application.class, args);
var userService = context.getBean(UserService.class);
userService.createUsersTable();
}
}
Now all the complexity is handled for you.
In fact if you do this you could even ditch the applicationContext.xml
in its totally. Rename your database.properties
to application.properties
and rename the properties.
spring.datasource.url=<jdbc url here from mysql.url>
spring.datasource.username=<value from mysql.username>
spring.datasource.password=<value from mysql.password>
Now ditch your applicationContext.xml
and the @ImportResource
from the Application
class and re-run. Spring Boot will automatically detect your services, dao, entities and configure itself accordingly.