detached entity passed to persist error while using #ManyToOne association - spring

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);
}
}

Related

EmptyResultDataAccessException when testing Spring controller

In my app, there is a controller, a service, a repo and a class. I am writing unit test to verify my PUT request. In postman, the put request works fine, however, when testing in JUnit test, it throws EmptyResultDataAccessException eror. Many other tests have the same problem and all of them require to find a specific entry in the repo by id. I think this is the problem. Please help me on this.
#Data
#Entity
public class ErrorMessage {
private #Id #GeneratedValue Long id;
private String message;
private int code;
public ErrorMessage() {
}
public ErrorMessage(int code, String message) {
this.code = code;
this.message = message;
}
}
#Repository
interface ErrorMessageRepository extends JpaRepository<ErrorMessage, Long> {
List<ErrorMessage> findByCode(int code);
}
#Service
public class ErrorMessageService {
#Autowired
ErrorMessageRepository repository;
#Transactional
public List<ErrorMessage> getAll()
{
return repository.findAll();
}
#Transactional(readOnly = true)
public Optional<ErrorMessage> getById(Long id)
{
return repository.findById(id);
}
#Transactional(readOnly = true)
public List<ErrorMessage> getByCode(int code)
{
return repository.findByCode(code);
}
#Transactional
public ErrorMessage saveOne(ErrorMessage messages)
{
return repository.save(messages);
}
#Transactional
public Optional<ErrorMessage> deleteById(long id)
{
Optional<ErrorMessage> em = repository.findById(id);
repository.deleteById(id);
return em;
}
#Transactional
public ErrorMessage updateById(long id, ErrorMessage newMessage)
{
ErrorMessage m = repository.findById(id).get();
m.setCode(newMessage.getCode());
m.setMessage(newMessage.getMessage());
repository.save(m);
return m;
}
}
class ErrorMessageController {
private static final Logger log = LoggerFactory.getLogger(ErrorMessageController.class);
#Autowired
ErrorMessageRepository repository;
#Autowired
private ErrorMessageService ems;
#GetMapping("/errormessages")
public List<ErrorMessage> getAll() {
return ems.getAll();
}
#GetMapping("/errormessagesbycode/{code}")
public List<ErrorMessage> getByCode(#PathVariable int code) {
return ems.getByCode(code);
}
#GetMapping("/errormessage/{id}")
ErrorMessage getById(#PathVariable Long id) {
return ems.getById(id)
.orElseThrow(() -> new MessageNotFoundException(id));
}
#PostMapping("/errormessage")
ErrorMessage newMessage(#RequestBody ErrorMessage newMessage) {
return ems.saveOne(newMessage);
}
#DeleteMapping("/errormessage/{id}")
Optional<ErrorMessage> deleteMessage(#PathVariable Long id) {
return ems.deleteById(id);
}
#PutMapping("/errormessage/{id}")
ErrorMessage updateMessage(#PathVariable Long id, #RequestBody ErrorMessage newMessage) {
return ems.updateById(id, newMessage);
}
}
#SpringBootTest
#AutoConfigureMockMvc
public class ErrorMessageTest {
private static ErrorMessage em, emId;
private static ObjectMapper mapper;
#Autowired
private MockMvc mockMvc;
#BeforeAll
public static void init() throws Exception {
mapper = new ObjectMapper();
em = new ErrorMessage(400, "bad request0");
emId = new ErrorMessage(400, "bad request0");
emId.setId(Long.valueOf(1));
}
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
}
Try this
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
ErrorMessageService errorMessageService = Mockito.mock(ErrorMessageService.class);
Mockito.when(errorMessageService.updateById(Mockito.any(), Mockito.any())).thenReturn(modifiedMessage);
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
I found out the bug. The order of the unit test is random. All i need to do is use #Order to ensure the order.

Tables not created in Cassandra db using springboot

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};
}
}

Return IDs in JSON response from Spring Data REST

I've got an entity
#Entity
#Table(name = "books")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "id", unique = true, nullable = false)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I initialize it like this
#PostConstruct
public void init() {
List<String> newFiles = this.listFiles();
newFiles.forEach(filename -> {
Book book = new Book();
book.setName(filename);
dbRepository.save(book);
});
}
If I set the result of save to an instance of Book, I can get the id and it is not null—so id is created fine.
I defined a repository
#RepositoryRestResource
public interface IBooksRepository extends CrudRepository<Book, Long> {
}
which I'd like to use to get and set data into the books table in the database.
When I try to access my repository rest using curl localhost:8080/books, I get this response
{
"_embedded":{
"books":[
{
"name":"simple-file.txt",
"_links":{
"self":{
"href":"http://localhost:8080/books/1"
},
"book":{
"href":"http://localhost:8080/books/1"
}
}
}
]
},
"_links":{
"self":{
"href":"http://localhost:8080/books"
},
"profile":{
"href":"http://localhost:8080/profile/books"
}
}
}
The books element returns name only. How can I make it return id too, on the same level as name?
Spring Data Rest hides the ID by default, in order to have it in the JSON you have to manually configure that for your entity. Depending on your spring version you can either provide your own configuration (old):
#Configuration
public class ExposeEntityIdRestConfiguration extends RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Book.class);
}
}
...or register a RepositoryRestConfigurer (current):
#Component
public class ExposeEntityIdRestMvcConfiguration extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Book.class);
}
}
See the Spring Data Rest documentation for more details.
The accepted answer overrides a deprecated method. Here's the updated version:
#Component
public class RestConfig implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.exposeIdsFor(Book.class);
}
}
An alternative approach is to implement RepositoryRestConfigurer in your #SpringBootApplication annotated class:
#SpringBootApplication
public class MyApplication implements RepositoryRestConfigurer {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.exposeIdsFor(Book.class);
}
}
There is now a static method RepositoryRestConfigurer.withConfig that does the same thing as above. See javadoc:
Convenience method to easily create simple {#link RepositoryRestConfigurer} instances that solely want to tweak the {#link RepositoryRestConfiguration}.
I found the usage in one of their integration tests
So the following approach would be more up to date as of now:
#Bean
public RepositoryRestConfigurer repositoryRestConfigurer()
{
return RepositoryRestConfigurer.withConfig(config -> {
config.exposeIdsFor(Book.class);
});
}
#Component
public class RestConfig implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Book.class);
//config.exposeIdsFor(Library.class);
}
}
This is a solution which works for all entities
#Autowired
private EntityManager entityManager;
#Bean
public RepositoryRestConfigurer repositoryRestConfigurer() {
return RepositoryRestConfigurer.withConfig(config -> config.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(Type::getJavaType).toArray(Class[]::new)));
}
This is a good way to go.
#Projection(name = "customBook", types = { Book.class })
public interface CustomBook {
#Value("#{target.id}")
long getId();
}
credit: https://www.baeldung.com/spring-data-rest-projections-excerpts

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

How to integrate wicket framework with mongoDB?

I have using spring-data-mongodb 1.2.1-RELEASE in quick start application. it is working fine,
i can connect to mongo db i can create,update and delete collection now i want to integrate wicket-framework with this application.
Domain class
#Document
public class Student
{
#Id
private String id;
private String firstName;
private int age;
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
}
Student repository class
#Repository
public class StudentService
{
#Autowired
MongoTemplate mongoTemplate;
#Override
public void create(Student student)
{
mongoTemplate.insert(student);
}
#Override
public void update(Query query, Update update)
{
mongoTemplate.updateFirst(query, update, Student.class);
}
#Override
public List<Student> findAll()
{
List<Student> students = mongoTemplate.findAll(Student.class);
logger.debug("Student: {}", students);
return students;
}
#Override
public void delete(Student student)
{
mongoTemplate.remove(student);
}
#Override
public void deleteAll()
{
Query searchUserQuery = new Query(Criteria.where("age").gt(0));
mongoTemplate.remove(searchUserQuery, Student.class);
}
}
Mongo configuration class
#Configuration
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {MongoDBApp.class})
#PropertySource("classpath:application.properties")
public class MongoConfiguration extends AbstractMongoConfiguration
{
#Override
protected String getDatabaseName()
{
return "demo";
}
#Override
public Mongo mongo() throws Exception
{
return new Mongo("localhost", 27017);
}
#Override
protected String getMappingBasePackage()
{
return "mypackage";
}
}
Mongo Db main class
public class MongoDBApp
{
static final Logger logger = LoggerFactory.getLogger(MongoDBApp.class);
public static void main(String[] args)
{
logger.info("Demo application");
ApplicationContext context = new AnnotationConfigApplicationContext(MongoConfiguration.class);
StudentService studentService = context.getBean(StudentService.class);
Student student = new Student();
student.setFirstName("foo");
student.setAge(24);
studentService.create(student);
List<Student> students = studentService.findAll();
logger.info("No. of students: {}", students.size());
studentService.delete(student);
logger.info("Deleted student: {}", student);
}
}
Dependencies in pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
For this code i want to integrate with wicket framework.
Please help me how to do that?
if your Spring/MongoDb works well you just need to integrate it into Wicket with module wicket-spring. You can find and example of this integration here:
http://wicketguide.comsysto.com/guide/chapter18.html#chapter18_2
The source is available here:
https://github.com/bitstorm/Wicket-tutorial-examples/tree/master/SpringInjectionExample
Thanks Andrea del bence,
I did like this in MyWicketApplication
#Override
public void init()
{
super.init();
getComponentInstantiationListeners().add(new SpringComponentInjector(this, getSpringContext()));
// add your configuration here
}
public ApplicationContext getSpringContext()
{
return WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
}

Resources