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
Related
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();
}
}
I would like to save a new entity using HazlecastRepository.
When the id is null, the KeyValueTemplate use SecureRandom and generate id which is like -123123123123123123.
I don't want to save id like that, instead of that i woud like to get it from sequence in db and put it to the map.
I have found 2 solutions:
1) In AdminService get the next value from sequence in database and set it
2) Create atomic counter id in the Hazelcast server and init it with the current value from the sequence. In AdminService get counter, increment value and set id.
but they are not very pretty.
Do you have any other ideas?
The code:
#Configuration
#EnableHazelcastRepositories(basePackages = "com.test")
public class HazelcastConfig {
#Bean
public HazelcastInstance hazelcastInstance(ClientConfig clientConfig) {
return HazelcastClient.newHazelcastClient(clientConfig);
}
#Bean
#Qualifier("client")
public ClientConfig clientConfig() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.setClassLoader(HazelcastConfig.class.getClassLoader());
ClientNetworkConfig networkConfig = clientConfig.getNetworkConfig();
networkConfig.addAddress("127.0.0.1:5701");
networkConfig.setConnectionAttemptLimit(20);
return clientConfig;
}
#Bean
public KeyValueTemplate keyValueTemplate(ClientConfig clientConfig) {
return new KeyValueTemplate(new HazelcastKeyValueAdapter(hazelcastInstance(clientConfig)));
}
}
#Service
#RequiredArgsConstructor
public class AdminService {
private final UserRepository userRepository;
...
#Transactional
public User addOrUpdateUser(UserUpdateDto dto) {
validate(dto);
User user = dto.getId() != null ? userService.getUser(dto.getId()) : new User();
mapUser(user, dto);
return userRepository.save(user);
}
...
}
#Repository
public interface UserRepository extends HazelcastRepository<User, Long> {
}
#KeySpace("users")
#Entity
#Table(name = "users)
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User extends DateAudit implements Serializable {
#javax.persistence.Id
#org.springframework.data.annotation.Id
// #GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_generator")
// #SequenceGenerator(name="user_generator", sequenceName = "user_seq")
private Long id;
...
}
Hazelcast server:
#Component
#Slf4j
public class UserLoader implements ApplicationContextAware, MapStore<Long, User> {
private static UserJpaRepository userJpaRepository;
#Override
public User load(Long key) {
log.info("load({})", key);
return userJpaRepository.findById(key).orElse(null);
}
#Override
public Map<Long, User> loadAll(Collection<Long> keys) {
Map<Long, User> result = new HashMap<>();
for (Long key : keys) {
User User = this.load(key);
if (User != null) {
result.put(key, User);
}
}
return result;
}
#Override
public Iterable<Long> loadAllKeys() {
return userJpaRepository.findAllId();
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
userJpaRepository = applicationContext.getBean(UserJpaRepository.class);
}
#Override
public void store(Long aLong, User user) {
userJpaRepository.save(user);
}
#Override
public void storeAll(Map<Long, User> map) {
for (Map.Entry<Long, User> mapEntry : map.entrySet()) {
store(mapEntry.getKey(), mapEntry.getValue());
}
}
#Override
public void delete(Long aLong) {
userJpaRepository.deleteById(aLong);
}
#Override
public void deleteAll(Collection<Long> collection) {
collection.forEach(this::delete);
}
}
public interface UserJpaRepository extends CrudRepository<User, Long> {
#Query("SELECT u.id FROM User u")
Iterable<Long> findAllId();
}
I think that there is no better way than what you described.
I'd go with the second solution, because then you're at least coupled to Hazelcast server only.
I tried to create tables in cassandra db on start-up of spring boot application but it doesn't seem to be able to create tables. Below is my configuration. I have the #EnableCassandraRepositories in my Application class. I already created my keyspace by default. So its just the tables that I'm looking to create.
Configuration
#Configuration
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${cassandra.contactpoints}")
private String contactPoints;
#Value("${cassandra.port}")
private int port;
#Value("${cassandra.keyspace}")
private String keySpace;
#Value("${cassandra.basePackages}")
private String basePackages;
#Autowired
private Environment environment;
#Override
protected String getKeyspaceName() {
return keySpace;
}
#Override
#Bean
public CassandraClusterFactoryBean cluster() {
final CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
return cluster;
}
#Override
#Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return new BasicCassandraMappingContext();
}
}
Entity
#Table
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class AssessmentAttemptDetailsEntity implements Serializable {
#PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED)
private String assessmentId;
#PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED)
private String attempid;
}
Application
#SpringBootApplication
#ComponentScan(basePackages = {"com.lte.assessmentanalytics.service","com.lte.assessmentanalytics.config", "com.lte.assessmentanalytics.model", "com.lte.assessmentanalytics.listener"})
#EnableCassandraRepositories("com.lte.assessmentanalytics.model")
public class AssessmentanalyticsApplication {
#Autowired
private AssessmentAttemptRepository assessmentAttemptRepository;
public static void main(String[] args) {
SpringApplication.run(AssessmentanalyticsApplication.class, args);
}
}
Repository
#Repository
public interface AssessmentAttemptRepository extends CassandraRepository<AssessmentAttemptDetailsEntity, Long> {
}
I was able to fix this by modifying my CassandraConfig class to.
#Configuration
#EnableCassandraRepositories("com.lte.assessmentanalytics.model")
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${cassandra.contactpoints}")
private String contactPoints;
#Value("${cassandra.port}")
private int port;
#Value("${cassandra.keyspace}")
private String keySpace;
#Value("${cassandra.basePackages}")
private String basePackages;
#Override
protected String getKeyspaceName() {
return keySpace;
}
#Override
protected String getContactPoints() {
return contactPoints;
}
#Override
protected int getPort() {
return port;
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
#Override
public String[] getEntityBasePackages() {
return new String[] {basePackages};
}
}
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);
}
I have Book & Bookdetail class
When I am trying to use same BookDetail information for my two Book objects using #ManyToOne, I am getting
"detached entity passed to persist error".
I tried same with JpaRepository and CrudRepository. But same result
Please help me. Thanks in advance
Main Class
#SpringBootApplication
public class HelloJpaApplication implements CommandLineRunner{
private static final Logger logger = LoggerFactory.getLogger(HelloJpaApplication.class);
#Autowired
private BookRepository bookRepository;
public void run(String... arg0) throws Exception {
Book book1=new Book();
book1.setName("my thoughts");
Book book2=new Book();
book2.setName("your thoughts");
Bookdetail detail=new Bookdetail();
detail.setCategory("good books");
book1.setBookdetail(detail);
book2.setBookdetail(detail);
bookRepository.save(book1);
bookRepository.save(book2);
}
public static void main(String[] args) {
SpringApplication.run(HelloJpaApplication.class, args);
}
}
BookRepository interface
public interface BookRepository extends JpaRepository<Book,Integer>{
}
Book class
#Entity
public class Book {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int bookid;
private String name;
#ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
private Bookdetail bookdetail;
public Book() {
}
public Book(String name) {
this.name=name;
}
public int getBookid() {
return bookid;
}
public void setBookid(int bookid) {
this.bookid = bookid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Bookdetail getBookdetail() {
return bookdetail;
}
public void setBookdetail(Bookdetail bookdetail) {
this.bookdetail = bookdetail;
}
}
Bookdetail Class
#Entity
#Table(name="bookdetail")
public class Bookdetail {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int bookid;
private String category;
public Bookdetail() {
}
public Bookdetail(String category) {
this.category=category;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
Every object created just with Bookdetail detail=new Bookdetail(); is a detached entity which mean he's not in the hibernate session or don't have a identifier id.
And you have to wrap your services which persist or update or delete into a transaction.
So first you must save detail your detached entity : bookdetailRepository.save(detail) to attached it to the session But in your case you already specify cascade = {CascadeType.PERSIST,CascadeType.MERGE}
#ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
private Bookdetail bookdetail;
No need to save it before, the job will be done automatically.
the solution then is to annotate the method run() with #Transactional
...
#Transactional
public void run(String... arg0)
...
bookdetailRepository.save(detail)
book1.setBookdetail(detail);
...
#Transactional before public void run() worked for me... Thank you very much #Youssef and #Chathuranga Tennakoon for your efforts. Sharing the code for others.
#SpringBootApplication
// #EnableJpaRepositories(basePackages = {"com.jpa.repository"})
public class HelloJpaApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(HelloJpaApplication.class);
#Autowired
private BookRepository bookRepository;
// #Autowired
// private BookDetailRepository bookDetailRepository;
#Transactional
public void run(String... arg0) throws Exception {
Book book1 = new Book();
book1.setName("my thoughts");
Book book2 = new Book();
book2.setName("your thoughts");
Bookdetail detail = new Bookdetail();
detail.setCategory("good books");
// bookDetailRepository.save(detail);
book1.setBookdetail(detail);
book2.setBookdetail(detail);
bookRepository.save(book1);
bookRepository.save(book2);
}
public static void main(String[] args) {
SpringApplication.run(HelloJpaApplication.class, args);
}
}
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int bookid;
private String name;
#ManyToOne(cascade = CascadeType.ALL)
private Bookdetail bookdetail;
public Book() {
}
public Book(String name) {
this.name = name;
}
public int getBookid() {
return bookid;
}
public void setBookid(int bookid) {
this.bookid = bookid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Bookdetail getBookdetail() {
return bookdetail;
}
public void setBookdetail(Bookdetail bookdetail) {
this.bookdetail = bookdetail;
}
}
#Entity
#Table(name = "bookdetail")
public class Bookdetail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int bookid;
private String category;
public Bookdetail() {
}
public Bookdetail(String category) {
this.category = category;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
#Repository
public interface BookRepository extends JpaRepository<Book,Integer>{
}
Add BookdetailRepository as follows.
#Repository
public interface BookdetailRepository extends JpaRepository<Bookdetail,Integer> {
}
And change the HelloJpaApplication as follows.
#SpringBootApplication
#EnableJpaRepositories(basePackages = {"com.jpa"})
public class HelloJpaApplication implements CommandLineRunner{
private static final Logger logger = LoggerFactory.getLogger(HelloJpaApplication.class);
#Autowired
private BookRepository bookRepository;
#Autowired
BookdetailRepository bookdetailRepository;
public void run(String... arg0) throws Exception {
Book book1=new Book();
book1.setName("my thoughts");
Book book2=new Book();
book2.setName("your thoughts");
Bookdetail detail=new Bookdetail();
detail.setCategory("good books");
bookdetailRepository.save(detail); //saving the bookDetail
book1.setBookdetail(detail);
book2.setBookdetail(detail);
bookRepository.save(book1);
bookRepository.save(book2);
}
public static void main(String[] args) {
SpringApplication.run(HelloJpaApplication.class, args);
}
}