월 24,200원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
left outer join 쿼리가 생략되는 이유
@SpringBootTest @Slf4j @Transactional class JpqlApplicationTests { @Autowired EntityManager em; @Test @Rollback(false) void contextLoads() { Team team = new Team(); team.setName("맨유"); em.persist(team); Member member1 = new Member(); member1.setUsername("박지성"); member1.setAge(20); member1.setTeam(team); Member member2 = new Member(); member2.setUsername("드록바"); member2.setAge(24); em.persist(member); em.persist(member2); em.flush(); em.clear(); List<Member> resultList = em.createQuery("select m from Member m left outer join m.team t", Member.class) .getResultList(); } }member와 team이 다대일 연관관계입니다.member1은 team과 연결해주었고 member2는 연결해주지않았습니다.inner join 쿼리는 잘나가나 left outer join을 하면 쿼리가 아래와 같이 나갑니다. select m1_0.member_id, m1_0.age, m1_0.team_id, m1_0.username from member m1_0 <질문>제 생각에는 left outer join 은 어떻게 보면 member만 조회하는 것이랑 같은 결과니까 JPA가 자가판단해서 그냥 member만 조회하는 쿼리만 보낸다고 생각이드는데 맞게 생각한 것인지 모르겠습니다.<참고>on t.name='맨유' 를 추가하면 쿼리가 잘 나갑니다.List<Member> resultList = em.createQuery("select m from Member m left join m.team t on t.name='맨유'", Member.class) .getResultList();select m1_0.member_id, m1_0.age, m1_0.team_id, m1_0.username from member m1_0 left join team t1_0 on t1_0.team_id=m1_0.team_id and t1_0.name='맨유'답변 주시면 정말 감사하겠습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
이럴 때는 엔티티 관계 설정을 1:1로 해야하는지 1:n 으로 해야하는지 궁금합니다.
[질문 내용]https://www.inflearn.com/questions/958528이전 질문에 이어서 질문 드립니다.이전 내용을 간단히 요약하면 보안매체 엔티티와 보안매체가 갖는 토큰을 엔티티로 표현할 때, 보안매체 한 개에 유효한 토큰은 하나만 가질 수 있으므로 1:1 관계로 해야할지, 아니면 사용자에 연결된(만료된 토큰과 현재 유효한 토큰) 모든 토큰을 가지고 온 후, 그 이후에 유효한 토큰을 소스상에서 필터해서 얻어내는 식으로 하여 1:n으로 해야할지 여쭈어보았는데요. 답변으로 1:n으로 관계를 짓고, 유효한 토큰을 필터하는 식으로 하길 권해주셨습니다.이게 '보안매체에 유효한 토큰은 오직 한개' 라는 개념적인 관점에서는 1:1이지만, JPA에서의 엔티티 클래스로 표현해보자면 하나의 고객KEY를 외래키로 하는 토큰은 여러개가 있을 수 있으니 1(단일객체)가 아닌 N(리스트)로 표현하고, 비즈니스 로직상에서 유효한 하나를 뽑아서 현재 활성화된 토큰을 얻어낸다고 보면 되는걸까요?매번 쿼리를 통해 필요로하는 객체로 뽑아내다가, 이를 엔티티 클래스, 관계로 소스를 짜려니 어색하네요. 항상 감사합니다. 답변주시면 감사드리겠습니다!! ㅠㅠ
- 해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
@JoinTable 기본키, 복합키
안녕하세요 @JoinTable 사용 중 궁금증이 생겨서 질문 남깁니다.연습중에 course와 category를 다대다로 연결하기 위해 @JoinTable을 사용했습니다. 추가적인 필드가 필요없다 생각해서 우선 다대다 관계를 풀어주는 엔티티를 만들지 않고 @JoinTable을 사용했습니다. 여기서 질문 입니다. 검색을 해본 결과 @JoinTable은 들어온 fk를 복합키로 사용한다고 보았습니다. 그렇다면 제 생각에는 예를들면 @JoinTable인 course_category에 (course_id = 1 , category_id = 1)인 데이터가 존재하면 (course_id = 1 , category_id = 1)라는 데이터가 중복될 수 없다고 생각했습니다. 하지만 중복된 데이터가 저장되는 것이 가능했습니다. 이 이유가 왜인지 궁금합니다!! @ManyToMany @JoinTable(name = "COURSE_CATEGORY", joinColumns = @JoinColumn(name = "course_id"), inverseJoinColumns = @JoinColumn(name = "category_id")) private List<Category> categoryList = new ArrayList<>(); @Id 필드를 사용하기 위해 category_course 엔티티를 따로 만드는 방법으로 수정 중에 있는데 위의 질문 내용을 짚고 넘어가고 싶어서 질문 남깁니다!
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
연관관계 주인을 FK로 잡는 구체적 이유가 잘 이해가 안됩니다.
강의의 예제에서 Team 엔티티의 List members를 주인으로 잡게되면 insert시 Member측에선 update가 날라갈 수 있다라고 강의에서 설명이 된 것 같은데 왜 insert시 update가 일어나지?라는 생각이 들어서 혹시 이부분 첨언해주시면 감사드리겠습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
페치 조인1 강의에 대한 질문있습니다.
try { Team teamA = new Team(); teamA.setName("팀A"); em.persist(teamA); Team teamB = new Team(); teamB.setName("팀B"); em.persist(teamB); Member member1 = new Member(); member1.setUsername("회원1"); member1.setTeam(teamA); em.persist(member1); Member member2 = new Member(); member2.setUsername("회원2"); member2.setTeam(teamA); em.persist(member2); Member member3 = new Member(); member3.setUsername("회원3"); member3.setTeam(teamB); em.persist(member3); em.flush(); em.clear(); String query = "select m from Member m"; List<Member> result = em.createQuery(query, Member.class) .getResultList(); for (Member member : result) { System.out.println("member = " + member.getUsername() + ", " + member.getTeam().getName()); }Hibernate: /* select m from Member m */ select member0_.id as id1_0_, member0_.age as age2_0_, member0_.TEAM_ID as team_id5_0_, member0_.type as type3_0_, member0_.username as username4_0_ from Member member0_ Hibernate: select team0_.id as id1_3_0_, team0_.name as name2_3_0_ from Team team0_ where team0_.id in ( ?, ? ) member = 회원1, 팀A member = 회원2, 팀A member = 회원3, 팀B회원1을 조회하면서 회원과 팀에 대한 select 쿼리가 나가고 회원1을 조회하면서 teamA가 영속성 컨텍스트에 올라갔으니 회원2는 1차 캐시에서 가져온다고 했습니다. 그러면 teamB는 영속성 컨텍스트에 없기 때문에 team에 대한 쿼리가 발생해야 하는데 발생하지 않습니다. 왜 강의와 다르게 쿼리가 발생하는지 궁금합니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
개인 토이 프로젝트 진행 중인데 일대일 매핑 관련하여 질문이 있습니다.
[질문 내용]보안매체와 토큰이라는 엔티티 클래스가 있습니다.보안매체 하나에 서버에서 부여받은 토큰을 따로 db에 저장하여 관리하려고 하는데요. 하나의 보안매체에서는 유효한 토큰 하나만 부여 가능하므로 OneToOne 관계로 관계를 맺었습니다. 그런데 이 토큰은 일정기간이 지나면 만료가 되고 결국 만료기간이 지나면 새로운 토큰을 생성해야합니다. 이렇게 된다면 하나의 보안매체에 여러개의 토큰이 매칭 될 수 있는데.. 이건 oneToMany 관계이고, 비지니스에서 쿼리 수행을 통해 만료 안된 토큰을 추출 해야하는 상황인걸까요? 조금 혼선이 옵니다 ㅠㅠ 도움 주시면 감사하겠습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
질문입니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]스프링에서 프록시는 따로 설정을 하지 않아도 내부적으로 작동이 되는게 맞을까요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
엔티티매니저는 스레드별로 다시 생성되나요?
영한님 JPA 책에서 아래 그림을 보면 스레드가 하나이고 두개의 레포지토리를 사용할때 두개의 엔티티매니저가 하나의 영속성컨텍스트를 사용한다고 나와있습니다. 하지만 테스트 결과(레포지토리에 각각의 엔티티매니저가 있지만 SharedEntityManagerCreator를 통해서(?) 하나의 엔티티매니저를 사용하는걸 확인 했습니다(하나의 SessionImpl을 사용하고 있었으며 내부에 영속성 컨텍스트도 공유함)제가 이해한바가 맞는지 봐주시면 감사하겠습니다.엔티티매니저는 Bean으로 등록되지 않는다.엔티티매니저프록시객체에 메서드를 호출하면 현재 트랜잭션에서 사용되고 있는 엔티티매니저가 있는지 확인후 없다면 팩토리를 통해 엔티티매니저를 생성하고 있다면 재사용한다.엔티티매니저팩토리(인터페이스)는 직접 빈으로 등록되지 않고 LocalContainerEntityManagerFactoryBean 가 빈으로 등록되고 내부 필드에 EntityManagerFactory를 가지고 있고 다른곳에서 생성하지 않기때문에 논리적으로 싱글톤이다 라고 할 수 있다 라고 이해했습니다. 질문이해한 내용에서 3번이 맞다면 LocalContainerEntityManagerFactoryBean 에는 프록시 팩토리와 네이티브 팩토리가 있는데 왜 나눠서 인스턴스 변수로 갖고있는걸까요?EntityManagerFactory를 @Autowired로 주입받을때 LocalContainerEntityManagerFactoryBean 가 프록시 엔티티매니저팩토리를 만들어서 넣어주는걸까요?사진에서 SimpleJpaRepository에 em은 디버깅에서 위에 사진처럼 나오고 있는데 이유는 LocalContainerEntityManagerFactoryBean에서 팩토리를 통해 프록시 객체를 생성해주기 때문인가요?엔티티매니저프록시에서 메소드가 호출되면 리플랙션으로 SharedEntityManagerCreator.invoke 메소드가 호출되서 실제 엔티티매니저를 실행한다고 이해하면 될까요?LocalContainerEntityManagerFactoryBean 의 역할을 모르겠습니다.ㅠㅠ 팩토리를 논리적 싱글톤으로 관리하기 위한 객체일까요?너무 궁금해서 이 새벽까지 디버깅하다보니 질문이 많습니다 ㅠㅠ 답변주시면 감사하겠습니다!
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
11분40초 부분 team, member쿼리가 나가지 않습니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]Hibernate: create table Member ( MEMBER_ID varchar(255) not null, TEAM_ID bigint, USERNAME varchar(255), primary key (MEMBER_ID) )Hibernate: create table Team ( TEAM_ID bigint not null, name varchar(255), primary key (TEAM_ID) ) create모드로 설정되어있고 이렇게 drop하고 create만 하고 강의처럼 insert 쿼리를 생성하지 않는데 어떤점이 문제일까요?public class JpaMain { public static void main(String[] args){ EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try{ Team team = new Team(); team.setName("TeamA"); em.persist(team); Member member = new Member(); member.setUsername("member1"); member.setTeamId(team.getId()); em.persist(member); tx.commit(); } catch(Exception e){ tx.rollback(); } finally { em.close(); } emf.close(); } }
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
질문입니다.
현재 maven 으로 작업을 하셨는데 이를 gradle 로 한다면 하이버네이트 + gradle 로 작업을 하게 되는데 이런 식으로 실무에 많이 사용이 되나요?maven + hibernate / gradle + hibernate / maven + 스프링 데이터 JPA / gradle + 스프링 데이터 JPA 과 같은 조합들을 고민해보다가 헷갈려서 질문 드립니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
일대일 관계에서 유니크 제약조건이 추가 질문
강의에서 "외래키에 데이터베이스 유니크 제약조건이 추가되어야 일대일 관계가 가능하다" 를 알아보고자 Member 클래스의 Locker 객체를 아래와 같이 만들었습니다. @OneToOne @JoinColumn(name="LOCKER_ID", unique = true) private Locker locker; unique 값의 default가 false라 true를 바꿔서 넣어보니 잘 작동했습니다.그 다음 빼면 어떻게 되는지 궁금하여 unique 값을 @OneToOne @JoinColumn(name="LOCKER_ID") private Locker locker; 위와 같이 설정하고 돌려보니 create table member ( locker_id bigint unique)unique = true를 했던 것처럼 쿼리를 JPA에서 만들어줬습니다.<질문>@OneToOne 일때 JPA에서 자동으로 unique = false(default) 를 unique = true로 만들어주는지 궁금합니다.답변주시면 정말 감사하겠습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
영속성 컨텍스트에 대해 질문이 있습니다.
https://www.inflearn.com/questions/789381/%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A7%A4%EB%8B%88%EC%A0%80-%EB%8F%99%EC%8B%9C%EC%84%B1 위에 질문과 답변을 봤을때는 엔티티매니저가 달라도 트랜잭션이 같으면 같은 영속성컨텍스트를 사용한다고 되어있습니다. 하지만 이 부분이 실제로 어떻게 이뤄질 수 있는것인지 이해되지 않습니다. 코드로 어떻게 되어져 있는지 찾아볼 수 있을까요?제가 찾아본 내용으로는 SessionImpl이라는 엔티티매니저 구현체에 아래 처럼 엔티티매니저를 넣어서 영속성컨텍스트를 생성하는 코드를 볼 수 있었습니다. 그럼 영속성 컨텍스트는 따로 만들어지는게 아닌가? 라는 생각도 하게되었습니다. protected StatefulPersistenceContext createPersistenceContext() { return new StatefulPersistenceContext( this ); } 트랜잭션에 따라 영속성 컨텍스트가 공유되고 스레드마다 영속성 컨텍스트가 어떻게 나눠질 수 있는지 궁금합니다.그리고 영속성컨텍스트는 프록시라도 Bean으로 등록되는데 실제 동작을 하는건 원본객체일테고 그럼 그 객체이 있는 영속성컨텍스트를 공유하게 되는것은 아닌가? 의문이 들었습니다.질문을 정리하겠습니다. 트랜잭션에 따라 영속성 컨텍스트가 어떻게 공유될 수 있나요?(확인할 수 있는코드나 기술이 있다면 말씀해주시면 감사하겠습니다.)Bean으로 등록된 엔티티매니저가 다른 상태를 유지할 수 있는 방법이 무엇인가요? 프록시라해도 원본객체를 통해 동작하는게 아닌가요?영속성컨텍스트는 프록시라도 Bean으로 등록되는데 실제 동작을 하는건 원본객체일테고 그럼 그 객체이 있는 영속성컨텍스트를 공유하게 되는것은 아닌가요?바보같은 질문일 수 있지만 답변해주시면 너무 감사하겠습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
Table "MEMBER" not found 오류 문의
안녕하세요, 5강 학습 중 오류가 발생해 문의드립니다.수업과 동일하게 진행했지만, 계속해서 MEMBER 테이블을 찾을 수 없다는 에러가 발생합니다.(Table "MEMBER" not found (this database is empty); SQL statement:)두번째 이미지처럼 MEMBER 테이블도 정상적으로 생성되어 있는 상태입니다.혹시 어떻게하면 DB를 제대로 연결할 수 있을지 문의드리고 싶습니다.감사합니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
엔티티매니저의 대해 질문이 있습니다!
EntityManager는 빈 스코프가 프록시로 설정된다고 알고있습니다. 하지만 결국은 원본 엔티티매니저를 사용하는것이라고 이해했습니다. 그러면 요청이 들어오면 엔티티매니저를 사용하는데 엔티티매니저 내에 영속성 컨텍스트가 있고 싱글톤으로 유지된다면 영속성컨텍스트가 다른 요청이랑 공유되는게 아닌가요?싱글톤으로 등록되어 있는데 영속성컨텍스트가 공유되지 않는 이유가 궁금합니다. 엔티티매니저가 빈인데 어떻게 요청마다 생성되는것인지 궁금합니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
이러한 상황에서는 복합키와 대리키 중 어느것을 사용해야 하나요?
안녕하세요 현재 한 사용자가 한 상점에 대해서 특정 유형의 주문을 몇 번 했는지 조회하는 기능을 구현하고 있습니다.처음에는 주문 테이블의 모든 튜플을 조회하면서 shopid, userid, orderType이 일치하는 경우 값을 누적하는 방식으로 구현을 했는데요,테이블이 갈수록 커지고 이러한 조회가 기획 상 굉장히 빈번하기 때문에 한 유저가 한 상점에 대해 특정 유형의 주문을 몇번 했는지 기억하는 별도의 테이블을 생성하기로 하였습니다.테이블은 userId, shopId, reservationCount, pickupCount, noShowCount 를 필드로 갖도록 구현했습니다.그런데 여기서 기본키를 설정해야 하는데 저는 userId, shopId를 복합키로 하는 것이 좋다고 생각했습니다. 그 이유는 이러한 통계성 테이블이 join을 하는 상황이 없고, 한 유저가 특정 샵을 처음 이용할 때만 튜플이 생기고 이후에는 필드 값만 변경이 이루어지며, 조회가 매우 빈번하기 때문에 userId, shopId를 기본키로 하여 인덱싱을 활용하면 좋다고 생각했기 때문입니다.근데 강사님이 설명하시기를 실무에서는 복합키를 거의 사용하지 않는다고 말씀해주셨는데 이러한 상황에서도 그냥 대리키를 쓰는 것이 낫나요?복합키를 활용한 인덱싱 vs 대리키 + shopId, userId 추가 인덱싱이 둘 중 하나를 적용할 거 같은데 무엇을 기준으로 어떻게 판단해야 할 지 잘 모르겠습니다. 도움 주시면 감사하겠습니다. 참고로 db는 mysql사용중입니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
Identity 생성 전략에서 em.persist(entity) 호출 시 동작 과정 질문
다른분의 질문을 참고하여 생각했을 때em.persist(entity) 호출 -> pk값이 null인 상태로 1차 캐시에 저장불가-> Insert 쿼리가 DB에 전달 -> DB에서 PK 값 생성 -> PK값을 조회해서(내부적으로 select 쿼리를 보냄) 영속성 컨텍스트의 1차 캐시에 저장, PK 값이 적용된 영속 엔티티가 초기값일때 스냅샷으로 사용 위의 처럼 생각이 드는데 맞게 생각한건지 궁금합니다.답변주시면 정말 감사하겠습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
Member 에 orders 를 넣는 구조
Member 객체에서 orders 를 넣는 구조는 좋지 않은 설계라고 하셨는데 여기에 궁금한 점이 있어서 질문합니다! (10분쯤) 이커머스 사이트를 볼때, 로그인을 하게 되면 마이페이지가 있고 마이페이지에는 그 회원의 주문내역이나 그동안 주문했던 것들을 볼 수 있는게 꼭 있는거 같습니다. 이럴때 Member 에 orders 라는 필드가 있어야 좀 더 효과적으로 가지고 올 수 있다고 생각하는데 영한님께서는 좋지 않은 설계라고 하셔서 제 생각이 틀린건지 궁금합니다!
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
EntityManager를 사용하는 방법
https://www.inflearn.com/questions/152202/%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4위에 답변을 보면@Autowired가 스프링 빈을 주입한다면, @PersistenceContext는 JPA 스펙에서 제공하는 기능인데, 영속성 컨텍스트를 주입하는 표준 애노테이션입니다. 라고 적혀있습니다.(1) 그럼 @Autowired로 EntityManager 를 사용시 영속성 컨텍스트를 만들지 못하는 건가요??근데 @Autowired로 받아도 em.persist(member); 가되는 거보면 아닌것 같다고 생각이 드는데...답변의 의미를 잘 모르겠습니다. (2) 구글링한 결과로는 둘의 차이는 쓰레드 간에 동시성문제를 해결하여 EntityManager를 공유하지 않는다로 보이는데 이것을 말씀하신 걸까요??
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
상속관계와 식별관계
ERD에서 1대 1 식별 관계로 설계된 경우엔티티 설계는 상속관계로 매핑해야하나요? 식별관계로 매핑해야하나요?이 둘의 차이가 뭔지 궁금합니다!
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
JPA 실무에서 복합키를 사용하는지 궁금합니다!
안녕하세요 현재 JPA 강의를 모두 듣고 프로젝트를 진행하려고 하는 한 학생입니다. 컨설턴트님이 말하시길, RDB에서는 Index를 통한 조회가 생명이다. 또한 매핑되는 테이블의 경우는 데이터가 계속해서 많이 쌓이기 때문에 auto_increment를 통한 Id 설정보다는 복합키를 설정해서 테이블을 생성하는게 조회 성능에서는 월등히 빠르다. 현재 프로젝트의 볼륨의 경우에는 둘 중 무엇을 선택할지는 자유라고 본다. 그러나 개발자로써 의미있는 테이블 설계를 하고싶다면 조금은 번거롭더라도 복합키로 설정하는 것이 좋다고 보는 견해다.라고 말씀을 하셨는데 JPA 에서도 @Embedded나 @IdClass를 사용한 복합키 사용이 효율적일까요? 또한 복합키를 사용하면 QueryDsl을 사용 할 수있는지 궁금합니다!!