Spring Boot integration mybatis annotations error - spring

When I run the following program I receive the following error. How can I solve it?
Description:
file [E:\program\github\demo\target\classes\com\example\demo\mapper\UserMapper.class] required a single bean, but 4 were found:
- &userMapper: defined in file [E:\program\github\demo\target\classes\com\example\demo\mapper\UserMapper.class]
- systemEnvironment: a programmatically registered singleton - contextParameters: a programmatically registered singleton - contextAttributes: a programmatically registered singleton
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Disconnected from the target VM, address: '127.0.0.1:58011', transport: 'socket'
Process finished with exit code 1
Here is my Spring Boot config file application.properties:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
spring.datasource.username=root
spring.datasource.password=123456
mybatis.type-aliases-package=com.example.demo.entity
DemoApplication.java -- bootstrap class
#SpringBootApplication
#EnableTransactionManagement
#MapperScan(value="com.example.demo.mapper")
public class DemoApplication {
#Autowired
private UserService userService;
#RequestMapping("add")
public void add(User user) {
userService.addUser(user);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
entity -- User.java
public class User implements Serializable {
private int userId;
private String username;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
'}';
}
}
Service -- UserService.java
public interface UserService {
void addUser(User user);
}
Service implementation -- UserServiceImpl.java
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserMapper userMapper;
#Override
#Transactional
public void addUser(User user) {
userMapper.insert(user);
}
}
Mybatis interface -- UserMapper.java
#Mapper
public interface UserMapper {
#Insert(
value = "insert into user values(#{user.userId},#{user.username})"
)
void insert(User user);
}

Related

Is it safe to call something in my RestController from my WebSocketHandler?

I guess this is a bit of a threadsafe question, as I'm not sure how Spring Boot handles beans and web service calls and incoming websocket data.
Is it safe to call controller.doSomething() from McpWebSocketHandler, if I know MCPController.ping() also calls it?
Also, if I have the GameUnitService Autowired in both MCPController and McpWebSocketHandler, will there be any thread contention?
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
#RestController
public class MCPController implements MqttListener {
Logger log = LoggerFactory.getLogger(MCPController.class);
#Autowired private JdbcTemplate jdbc;
#Autowired private GameUnitService gameUnitService;
#GetMapping("/ping")
public Object ping(HttpServletRequest request) {
String remoteAddr = request.getHeader(FORWARDED) == null ? request.getRemoteAddr() : request.getRemoteAddr() + "/" + request.getHeader(FORWARDED);
log.info("Got ping from {}", remoteAddr);
doSomething();
return new Response(true, ErrorCode.OK, "pong");
}
public String doSomething() {
return gameUnitService.doSomethingWithDatabase();
}
}
public class McpWebSocketHandler extends AbstractWebSocketHandler {
private ApplicationContext appContext;
private GameUnitService gameUnitService;
private MCPController controller;
public McpWebSocketHandler(ApplicationContext appContext,GameUnitService gameUnitService) {
this.appContext = appContext;
this.gameUnitService = gameUnitService;
controller = (MCPController)appContext.getBean("MCPController");
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
String response = controller.doSomething(); //is this safe?
session.sendMessage(new TextMessage(response));
}
}
#Configuration
#EnableWebSocket
#ComponentScan(basePackageClasses = McpApplication.class)
//public class McpWebSocketConfig implements WebSocketConfigurer, MqttListener {
public class McpWebSocketConfig implements WebSocketConfigurer {
private static final Logger log = LoggerFactory.getLogger(McpWebSocketConfig.class);
#Autowired private ApplicationContext appContext;
#Autowired private GameUnitService gameUnitService;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new McpWebSocketHandler(appContext,gameUnitService), "/socket").setAllowedOrigins("*");
registry.addHandler(new McpWebSocketHandler(appContext,gameUnitService), "/").setAllowedOrigins("*");
}
...
}

Spring Security method security not working: PreAuthorize allowing all users instead of limited users

I am trying to implement method level security using spring security. I have annotated 2 separate methods with the #PreAuthorize annotation. In this example, I have 2 users ADMIN and USER. And I have restricted 2 methods both to each of the users. When I try logging in as USER I am able to access both the endpoint restricted to USER (getSomeTextForUser()) as well as to ADMIN(getSomeTextForAdmin()). So this is definitely not right and after viewing multiple tutorials I have not seen the error in my ways.
Expected behavior: person logged in as USER should get an error when trying to access the endpoint /test/admin since it calls getSomeTextForAdmin(). And the similar behavior should happen for the admin when calling /test/user since it calls getSomeTextForUser().
Main class
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
My controller class
#RestController
public class UserController {
#GetMapping("/")
public String home() {
return ("<h1> Welcome </h1>");
}
#GetMapping("/test/admin")
public String test() {
return getSomeTextForAdmin();
}
#GetMapping("/test/user")
public String test2() {
return getSomeTextForUser();
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomeTextForAdmin() {
return "For Admin Only!";
}
#PreAuthorize("hasRole('ROLE_USER')")
public String getSomeTextForUser() {
return "For User Only!";
}
}
The security configuration where I've enabled the prePost feature
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/test").hasAnyRole("ADMIN", "USER")
.antMatchers("/").permitAll()
.and().formLogin();
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
My User details service where I've just placed some default users in memory on startup for testing.
#Repository
public class UserRepositoryImpl implements UserRepository {
Map<String, User> users = new HashMap<>();
public UserRepositoryImpl() {
createDefaultUsers();
}
#Override
public Optional<User> findByUserName(String userName) {
return Optional.of(users.get(userName));
}
private void createDefaultUsers() {
users.put("admin", new User("admin", "pass", "ADMIN"));
users.put("user", new User("user", "pass", "USER"));
}
}
MyUserDetails is here
public class MyUserDetails implements UserDetails {
private final String userName;
private final String password;
private final List<GrantedAuthority> authorities;
public MyUserDetails(User user) {
this.userName = user.getUserName();
this.password = user.getPassword();
this.authorities = Arrays.stream(user.getRoles().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return userName;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
And the user class itself
#Entity
#Table(name = "User")
public class User {
public User(String userName, String password, String roles) {
this.userName = userName;
this.password = password;
this.roles = roles;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
private String userName;
private String password;
private boolean active;
private String roles;
}
First of all: why do you need this line in your configuration?
.antMatchers("/test").hasAnyRole("ADMIN", "USER")
You don't even have /test endpoint in your controller.
Second thing:
#RestController
public class UserController {
#GetMapping("/")
public String home() {
return ("<h1> Welcome </h1>");
}
#GetMapping("/test/admin")
public String test() {
return getSomeTextForAdmin();
}
#GetMapping("/test/user")
public String test2() {
return getSomeTextForUser();
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomeTextForAdmin() {
return "For Admin Only!";
}
#PreAuthorize("hasRole('ROLE_USER')")
public String getSomeTextForUser() {
return "For User Only!";
}
}
It shows you don't understand what Spring Proxy is. Unless you learn it, soon or later you will fall into problems.
I really encourge you to read about it but for now one takeaway to remember:
Annotated methods must be called from different class. In your case you call annotated methods from the same class and Spring doesn't care about any annotation.
You should use somtehing like this:
#Service
public class UserService {
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomeTextForAdmin() {
return "For Admin Only!";
}
#PreAuthorize("hasRole('ROLE_USER')")
public String getSomeTextForUser() {
return "For User Only!";
}
}
#RestController
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/")
public String home() {
return ("<h1> Welcome </h1>");
}
#GetMapping("/test/admin")
public String test() {
return userService.getSomeTextForAdmin();
}
#GetMapping("/test/user")
public String test2() {
return userService.getSomeTextForUser();
}
}

How to use Spring boot AutoWired and ScheduledExecutorService?

I need to use autowired in more than one class with ScheduledExecutorService, what I have tried is shown in this code. logging size of User list in below example always shows 0, even after user added to arraylist. How to properly use Autowired and ScheduledExecutorService in spring boot?
#Component
public class AnotherClass {
List<User> users = new ArrayList();
public void addUser(User user){
users.add(user);
}
public void logUsers(){
logger.info("User size " + users.size()); <================= Always logs 0, when called from executor
}
}
#RestController
public class SecondClass {
#Autowired
private AnotherClass anotherClass;
#GetMapping(value="/user/test")
public void logUsers(){
anotherClass.addUser(new User());
}
}
Application Class
#Component
#SpringBootApplication
public class SpringBootDemoApplication {
private ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
#Autowired
private AnotherClass anotherClass;
#PostConstruct
public void init() {
logger();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
public void logger(){
exec.scheduleAtFixedRate(new Runnable(){
#Override
public void run(){
try {
anotherClass.logUsers();
}catch (Exception e){
}
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}
The code works if you use the Spring #Autowired and not the #AutoWired Annotation.

Couldn't find PersistentEntity for type class when using #EnableMongoAuditing

I am getting "Couldn't find PersistentEntity for type class" error when I am using #EnableMongoAuditing features along with MongoRepository.
This happens when I save a document when collection isn't already present in database.
I tried whatever is mentioned in:
https://github.com/spring-projects/spring-boot/issues/12023
https://jira.spring.io/browse/DATAMONGO-1999
Spring boot mongodb auditing error
but nothing is working.
Mentioned things are:
Extend MongoConfig by AbstractMongoConfiguration and override all methods.
Here is my code which reproduced the same error:
MongoConfig class
#Configuration
public class MongoConfig extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Override
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
#Override
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
#Override
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
#Override
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
#Override
protected String getDatabaseName() {
return mongoDB;
}
}
Person Collection class
#Document
public class Person {
#Id
private String id;
private String name;
#CreatedDate
private LocalDateTime createdAt;
#LastModifiedDate
private LocalDateTime lastModified;
// Getter Setters Constructors omitted for brevity
}
Main Application class
#EnableMongoAuditing
#EnableMongoRepositories ({"com.example.*", "org.apache.*"})
#SpringBootApplication
#ComponentScan({"com.example.*", "org.apache.*"})
public class DemoApplication implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Expected Result is Person entity should be saved in database.
Actual Result is "Couldn't find PersistentEntity for type class Person" error
Looks like you ran into https://github.com/spring-projects/spring-boot/issues/12023
Extending AbstractMongoConfiguration will switch off Spring Boot's auto-configuration of various Mongo components and also customises the base packages that are used to scan for mappings. I would recommend that you don't use it in Spring Boot.
Update
I managed to get the example running with the configuration as simple as
#Configuration
public class MongoConfig {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
#Bean
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
}
and the app class
#EnableMongoAuditing
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Thread.sleep(2000);
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Notice that I followed my own advice and did't inherit from AbstractMongoConfiguration
Explanation
The problem lies in the initialization of
#Bean
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
You simply call MongoMappingContext constructor, without calling setInitialEntitySet. Compare that with MongoDataConfiguration auto-configuration class.
#Bean
#ConditionalOnMissingBean
public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions)
throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(this.applicationContext)
.scan(Document.class, Persistent.class));
Class<?> strategyClass = this.properties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiateClass(strategyClass));
}
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
Even worse, you don't register MongoMappingContext as a managed bean.
Due to this fact, auto-configuration class is still created. This leads to a race condition, I tried to run the original code and could easily reproduce the error, but with a breakpoint in AbstractMappingContext.addPersistentEntity the test always passed.
For me I resolved this issue by adding following method in MongoConfig if your class extends from AbstractMongoConfiguration
#Override
protected String getMappingBasePackage() {
return "com.companyName.modulename"
}
If MongoConfig extends from MongoConfigurationSupport then add below method
#Override
protected Collection<String> getMappingBasePackages() {
return Arrays.asList("com.companyName.module1","com.companyName.module2");
}
Note that in later case I can specify multiple package names as base packages.

How to write custom queries in spring CRUD Repository

I am developing an app using spring Boot.
Here is my code.
UserSample.java
#Entity
public class UserSample {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long userId;
private String userName;
public UserSample() {
super();
}
public UserSample(String userName) {
super();
this.userName = userName;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Interface is:
public interface UserSampleRepository extends CrudRepository<UserSample, Long> {
}
In main class
#SpringBootApplication
public class MainApplication implements CommandLineRunner {
#Autowired
UserSampleRepository usersampleRepo;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
List<UserSample> userSample = new LinkedList<UserSample>();
// load data to the table
userSample.add(new UserSample("user1"));
userSample.add(new UserSample("user2"));
usersampleRepo.save(userSample);
UserSample userInfo = usersampleRepo.findOne(1);
}
}
I am having in-memory database. Here I am trying to write a query to retrieve by username. like userSampleRepo.findByUserName(String username);
I tried many ways but nothing worked for me.any suggestions?
I tried adding a method in interface .
public interface UserSampleRepository extends CrudRepository<UserSample, Long> {
UserSample findByUserName(String username);
}
created another class.
#Repository
public class UserSampleRepositoryImpl implements UserSampleRepository {
#PersistenceContext
private EntityManager em;
#Override
public UserSample findByUserName(String username) {
TypedQuery<UserSample> query = em.createQuery("select c from UserSample c where c.userName = :username",
UserSample.class);
query.setParameter("username", username);
return query.getSingleResult();
}
In main class, I used this statement
#Autowired
UserSampleRepositoryImpl usersampleRepo;
UserSample userInfo = usersampleRepo.findByUserName("user1");
I am getting this error:
"java.lang.IllegalStateException: Failed to execute CommandLineRunner"
aused by: org.springframework.dao.EmptyResultDataAccessException: No entity found for query; nested exception is javax.persistence.NoResultException: No entity found for query

Resources