Reputation: 416
Problem: I am setting up Spring + JPA + MYSQL database. However, once I've set it up, I always get the following error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'lt.robot.dao.UserJPADaoImpl' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:353)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1090)
at lt.robot.main.App.main(App.java:15)
This problem was solved here. Because my config file is in Java, I've added annotation (below) and now it works.
@EnableAspectJAutoProxy(proxyTargetClass = true)
Question: Why do I need it? I've looked at "Spring in Action 4th edition", "Spring Data", numerous online tutorial, but none of them annotates Config class with AspectJProxy. How does my application setup differ from those found online (even Spring's github examples).
My Setup
Package structure:
lt.robot.main
App.class
AppConfig.class
lt.robot.entity
User.class
lt.robot.dao
UserJPADao.interface
UserJPADaoImpl.class
pom.xml dependencies
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Brussels-SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.0.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
</dependencies>
App.class
import lt.robot.dao.UserJPADaoImpl;
import lt.robot.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
UserJPADaoImpl userDaoImpl = ctx.getBean(UserJPADaoImpl.class);
System.out.println(userDaoImpl.get(2));
User userNew = new User();
userNew.setUser_name("NewUser");
userDaoImpl.add(userNew);
}
}
AppConfig.class
@Configuration
@EnableTransactionManagement
@ComponentScan("lt.robot")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
@Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring1");
dataSource.setUsername("root");
dataSource.setPassword("pass");
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
HibernateJpaVendorAdapter vendorAdapter = new
HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.MYSQL);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory =
new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("lt.robot.entity");
factory.setDataSource(getDataSource());
return factory;
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(){
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
}
User.class
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private int user_id;
@Column(name = "user_name")
private String user_name;
//Getters & Setters
}
UserJPADaoImpl.class
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import lt.robot.dao.UserJPADao;
import lt.robot.entity.User;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional(transactionManager = "transactionManager")
public class UserJPADaoImpl implements UserJPADao {
@PersistenceContext
private EntityManager em;
public void add(User t) {
em.persist(t);
}
public User get(int id) {
return em.find(User.class, id);
}
}
Upvotes: 2
Views: 1163
Reputation: 4024
Your answer lies here and here (bean proxy mechanism used by Spring)
Since UserJPADaoImpl
class is marked @Transactional
default JDK interface based proxy is created for it as it implements interface UserJPADao
.
The statement ctx.getBean(UserJPADaoImpl.class)
signals Spring to specifically look for the bean of type UserJPADaoImpl
only and not of UserJPADao
; which is what created by default as explained above.
Thus Spring application context could not find any bean of type UserJPADaoImpl
and the below exception is encountered.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'lt.robot.dao.UserJPADaoImpl' available
Thus to signal Spring to look for specific bean type you can have either of below
ctx.getBean(UserJPADaoImpl.class)
look up bean of it's interface type i.e. UserJPADao
via ctx.getBean(UserJPADao.class)
@EnableAspectJAutoProxy(proxyTargetClass = true)
Mostly in tutorials the first approach is used because of which you don't see any mention of @EnableAspectJAutoProxy(proxyTargetClass = true)
there as they rely on default JDK interface based proxies and bean lookup via interface instead of its implementation.
Let know in comments if you need any further information.
Upvotes: 2
Reputation: 4084
Since you are using Spring-Data-JPA , I would suggest you to create repository using JPARepository
inteface like this:
@Repository
public interface UserRepository extends JpaRepository<User, Serializable>{
User findByUser_id(int id);
}
And in your database configuration use @EnableJpaRepositories
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.example.repository")
public class AppConfig {
..
..
..
}
Create a service and then autowire UserRepository
like this and use its method .
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public User saveUser(User user){
return userRepository.save(user);
}
public List<User> getAllUsers(){
return userRepository.getAll();
}
public User getUserById(int id){
return userRepository.findByUser_id(id);
}
}
Create a controller and autowire UserService
and use its method.
Upvotes: 0